diff options
author | Erik Schilling <ablu.erikschilling@googlemail.com> | 2012-04-03 13:29:05 +0200 |
---|---|---|
committer | Erik Schilling <ablu.erikschilling@googlemail.com> | 2012-04-04 16:22:11 +0200 |
commit | f8e816d9185c09d1c17d921b775e483d132982e5 (patch) | |
tree | 3ab299ab6057db3bfd8feb24130a6fcb7e64a60d /src/game-server | |
parent | e4baa92aae537921dd17873328a95ab17afcfdfc (diff) | |
download | manaserv-f8e816d9185c09d1c17d921b775e483d132982e5.tar.gz manaserv-f8e816d9185c09d1c17d921b775e483d132982e5.tar.bz2 manaserv-f8e816d9185c09d1c17d921b775e483d132982e5.tar.xz manaserv-f8e816d9185c09d1c17d921b775e483d132982e5.zip |
Enhanced special support
- Made the current charge being saved.
- Added script binds:
- chr_set_special_recharge_speed
- chr_get_special_recharge_speed
- chr_set_special_mana
- chr_get_special_mana
- get_special_info
- Added special info lua class. Functions:
- name
- needed_mana
- rechargeable
- on_use
- on_recharged
- category
Further the engine no longer sets charge to 0 after using of specials
this allows more flexbilillity (like failing specials).
Changes on the xml database:
- recharge renamed to rechargeable (needed by client and server)
- needed - the needed mana to trigger a special (server only)
- rechargespeed - the defailt recharge speed in mana per tick (server only)
- target - the type of target (either being or point) (server and client)
I also made the lua engine pushing nil instead of a 0 light userdata when
the pointer was 0.
Database update needed.
Change is tested.
Mana-Mantis: #167, #156
Reviewed-by: bjorn.
Diffstat (limited to 'src/game-server')
-rw-r--r-- | src/game-server/character.cpp | 203 | ||||
-rw-r--r-- | src/game-server/character.h | 63 | ||||
-rw-r--r-- | src/game-server/commandhandler.cpp | 200 | ||||
-rw-r--r-- | src/game-server/gamehandler.cpp | 48 | ||||
-rw-r--r-- | src/game-server/gamehandler.h | 3 | ||||
-rw-r--r-- | src/game-server/main-game.cpp | 6 | ||||
-rw-r--r-- | src/game-server/specialmanager.cpp | 177 | ||||
-rw-r--r-- | src/game-server/specialmanager.h | 114 |
8 files changed, 713 insertions, 101 deletions
diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp index 1a46ce55..59021622 100644 --- a/src/game-server/character.cpp +++ b/src/game-server/character.cpp @@ -76,7 +76,6 @@ Character::Character(MessageIn &msg): mClient(NULL), mConnected(true), mTransactionHandler(NULL), - mRechargePerSpecial(0), mSpecialUpdateNeeded(false), mDatabaseID(-1), mHairStyle(0), @@ -108,12 +107,6 @@ Character::Character(MessageIn &msg): Inventory(this).initialize(); modifiedAllAttribute(); setSize(16); - - // Give the character some specials for testing. - //TODO: Get from quest vars and equipment - giveSpecial(1); - giveSpecial(2); - giveSpecial(3); } Character::~Character() @@ -138,26 +131,22 @@ void Character::update() return; // Update special recharge - std::list<Special *> rechargeNeeded; - int numRechargeNeeded = 0; - for (std::map<int, Special*>::iterator i = mSpecials.begin(); - i != mSpecials.end(); i++) - { - Special * s = i->second; - if (s->currentMana < s->neededMana) - { - rechargeNeeded.push_back(s); - numRechargeNeeded++; - } - } - if (numRechargeNeeded > 0) + for (SpecialMap::iterator it = mSpecials.begin(), it_end = mSpecials.end(); + it != it_end; it++) { - mRechargePerSpecial = getModifiedAttribute(ATTR_INT) - / numRechargeNeeded; - for (std::list<Special*>::iterator i = rechargeNeeded.begin(); - i != rechargeNeeded.end(); i++) + SpecialValue &s = it->second; + if (s.specialInfo->rechargeable && s.currentMana < s.specialInfo->neededMana) { - (*i)->currentMana += mRechargePerSpecial; + s.currentMana += s.rechargeSpeed; + if (s.currentMana >= s.specialInfo->neededMana && + s.specialInfo->rechargedCallback.isValid()) + { + Script *script = ScriptManager::currentState(); + script->prepare(s.specialInfo->rechargedCallback); + script->push(this); + script->push(s.specialInfo->id); + script->execute(); + } } } @@ -263,50 +252,135 @@ void Character::respawn() GameState::enqueueWarp(this, MapManager::getMap(spawnMap), spawnX, spawnY); } -void Character::useSpecial(int id) +bool Character::specialUseCheck(SpecialMap::iterator it) { - //check if the character may use this special in general - std::map<int, Special*>::iterator i = mSpecials.find(id); - if (i == mSpecials.end()) + if (it == mSpecials.end()) { - LOG_INFO("Character uses special "<<id<<" without autorisation."); - return; + LOG_INFO("Character uses special " << it->first + << " without authorization."); + return false; } //check if the special is currently recharged - Special *special = i->second; - if (special->currentMana < special->neededMana) + SpecialValue &special = it->second; + if (special.specialInfo->rechargeable && + special.currentMana < special.specialInfo->neededMana) { - LOG_INFO("Character uses special "<<id<<" which is not recharged. (" - <<special->currentMana<<"/"<<special->neededMana<<")"); - return; + LOG_INFO("Character uses special " << it->first << " which is not recharged. (" + << special.currentMana << "/" + << special.specialInfo->neededMana << ")"); + return false; } + if (!special.specialInfo->useCallback.isValid()) + { + LOG_WARN("No callback for use of special " + << special.specialInfo->setName << "/" + << special.specialInfo->name << ". Ignoring special."); + return false; + } + return true; +} + +void Character::useSpecialOnBeing(int id, Being *b) +{ + SpecialMap::iterator it = mSpecials.find(id); + if (!specialUseCheck(it)) + return; + SpecialValue &special = it->second; + + if (special.specialInfo->target != SpecialManager::TARGET_BEING) + return; + + //tell script engine to cast the spell + Script *script = ScriptManager::currentState(); + script->setMap(getMap()); + script->prepare(special.specialInfo->useCallback); + script->push(this); + script->push(b); + script->push(special.specialInfo->id); + script->execute(); +} + +void Character::useSpecialOnPoint(int id, int x, int y) +{ + SpecialMap::iterator it = mSpecials.find(id); + if (!specialUseCheck(it)) + return; + SpecialValue &special = it->second; + + if (special.specialInfo->target != SpecialManager::TARGET_POINT) + return; + //tell script engine to cast the spell - special->currentMana = 0; - ScriptManager::performSpecialAction(id, this); - mSpecialUpdateNeeded = true; - return; + Script *script = ScriptManager::currentState(); + script->setMap(getMap()); + script->prepare(special.specialInfo->useCallback); + script->push(this); + script->push(x); + script->push(y); + script->push(special.specialInfo->id); + script->execute(); +} + +bool Character::giveSpecial(int id, int currentMana) +{ + if (mSpecials.find(id) == mSpecials.end()) + { + const SpecialManager::SpecialInfo *specialInfo = + specialManager->getSpecialInfo(id); + if (!specialInfo) + { + LOG_ERROR("Tried to give not existing special id " << id << "."); + return false; + } + mSpecials.insert(std::pair<int, SpecialValue>( + id, SpecialValue(currentMana, specialInfo))); + mSpecialUpdateNeeded = true; + return true; + } + return false; +} + +bool Character::setSpecialMana(int id, int mana) +{ + SpecialMap::iterator it = mSpecials.find(id); + if (it != mSpecials.end()) + { + it->second.currentMana = mana; + mSpecialUpdateNeeded = true; + return true; + } + return false; +} + +bool Character::setSpecialRechargeSpeed(int id, int speed) +{ + SpecialMap::iterator it = mSpecials.find(id); + if (it != mSpecials.end()) + { + it->second.rechargeSpeed = speed; + mSpecialUpdateNeeded = true; + return true; + } + return false; } void Character::sendSpecialUpdate() { //GPMSG_SPECIAL_STATUS = 0x0293, // { B specialID, L current, L max, L recharge } - for (std::map<int, Special*>::iterator i = mSpecials.begin(); - i != mSpecials.end(); i++) - { - - MessageOut msg(GPMSG_SPECIAL_STATUS ); - msg.writeInt8(i->first); - msg.writeInt32(i->second->currentMana); - msg.writeInt32(i->second->neededMana); - msg.writeInt32(mRechargePerSpecial); - /* Yes, the last one is redundant because it is the same for each - special, but I would like to keep the netcode flexible enough - to allow different recharge speed per special when necessary */ - gameHandler->sendTo(this, msg); + + MessageOut msg(GPMSG_SPECIAL_STATUS); + for (SpecialMap::iterator it = mSpecials.begin(), it_end = mSpecials.end(); + it != it_end; ++it) + { + msg.writeInt8(it->first); + msg.writeInt32(it->second.currentMana); + msg.writeInt32(it->second.specialInfo->neededMana); + msg.writeInt32(it->second.rechargeSpeed); } + gameHandler->sendTo(this, msg); } int Character::getMapId() const @@ -747,34 +821,19 @@ void Character::disconnected() } } -void Character::giveSpecial(int id) -{ - if (mSpecials.find(id) == mSpecials.end()) - { - Special *s = new Special(); - ScriptManager::addDataToSpecial(id, s); - mSpecials[id] = s; - mSpecialUpdateNeeded = true; - } -} - -void Character::takeSpecial(int id) +bool Character::takeSpecial(int id) { - std::map<int, Special*>::iterator i = mSpecials.find(id); + SpecialMap::iterator i = mSpecials.find(id); if (i != mSpecials.end()) { - delete i->second; mSpecials.erase(i); mSpecialUpdateNeeded = true; + return true; } + return false; } void Character::clearSpecials() { - for (std::map<int, Special*>::iterator i = mSpecials.begin(); - i != mSpecials.end(); i++) - { - delete i->second; - } mSpecials.clear(); } diff --git a/src/game-server/character.h b/src/game-server/character.h index eb8e432f..ec3fc737 100644 --- a/src/game-server/character.h +++ b/src/game-server/character.h @@ -24,8 +24,12 @@ #include "common/defines.h" #include "common/inventorydata.h" #include "common/manaserv_protocol.h" + #include "game-server/being.h" +#include "game-server/specialmanager.h" + #include "scripting/script.h" + #include "utils/logger.h" #include <map> @@ -39,18 +43,26 @@ class MessageOut; class Point; class Trade; -struct Special +struct SpecialValue { - Special() - : currentMana(0) - , neededMana(0) + SpecialValue(unsigned int currentMana, + const SpecialManager::SpecialInfo *specialInfo) + : currentMana(currentMana) + , rechargeSpeed(specialInfo->defaultRechargeSpeed) + , specialInfo(specialInfo) {} - int currentMana; - int neededMana; + unsigned int currentMana; + unsigned int rechargeSpeed; + const SpecialManager::SpecialInfo *specialInfo; }; /** + * Stores specials by their id. + */ +typedef std::map<unsigned int, SpecialValue> SpecialMap; + +/** * The representation of a player's character in the game world. */ class Character : public Being @@ -82,15 +94,37 @@ class Character : public Being void respawn(); /** - * makes the character perform a special action + * makes the character perform a special action on a being + * when it is allowed to do so + */ + void useSpecialOnBeing(int id, Being *b); + + /** + * makes the character perform a special action on a map point * when it is allowed to do so */ - void useSpecial(int id); + void useSpecialOnPoint(int id, int x, int y); /** * Allows a character to perform a special action */ - void giveSpecial(int id); + bool giveSpecial(int id, int currentMana = 0); + + /** + * Sets new current mana + makes sure that the client will get informed. + */ + bool setSpecialMana(int id, int mana); + + /** + * Gets the special value by id + */ + SpecialMap::iterator findSpecial(int id) + { return mSpecials.find(id); } + + /** + * Sets recharge speed of a special + */ + bool setSpecialRechargeSpeed(int id, int speed); /** * Removes all specials from character @@ -105,7 +139,7 @@ class Character : public Being /** * Removes an available special action */ - void takeSpecial(int id); + bool takeSpecial(int id); /** * Gets client computer. @@ -289,10 +323,10 @@ class Character : public Being int getSpecialSize() const { return mSpecials.size(); } - const std::map<int, Special*>::const_iterator getSpecialBegin() const + const SpecialMap::const_iterator getSpecialBegin() const { return mSpecials.begin(); } - const std::map<int, Special*>::const_iterator getSpecialEnd() const + const SpecialMap::const_iterator getSpecialEnd() const { return mSpecials.end(); } /** @@ -393,6 +427,8 @@ class Character : public Being { return BLOCKTYPE_CHARACTER; } private: + bool specialUseCheck(SpecialMap::iterator it); + double getAttrBase(AttributeMap::const_iterator it) const { return it->second.getBase(); } double getAttrMod(AttributeMap::const_iterator it) const @@ -461,11 +497,10 @@ class Character : public Being std::map<int, int> mExperience; /**< experience collected for each skill.*/ - std::map<int, Special*> mSpecials; + SpecialMap mSpecials; std::map<int, int> mStatusEffects; /**< only used by select functions to make it easier to make the accountserver do not modify or use anywhere else*/ - int mRechargePerSpecial; bool mSpecialUpdateNeeded; int mDatabaseID; /**< Character's database ID. */ diff --git a/src/game-server/commandhandler.cpp b/src/game-server/commandhandler.cpp index 90d242da..b48f1188 100644 --- a/src/game-server/commandhandler.cpp +++ b/src/game-server/commandhandler.cpp @@ -32,6 +32,7 @@ #include "game-server/mapmanager.h" #include "game-server/monster.h" #include "game-server/monstermanager.h" +#include "game-server/specialmanager.h" #include "game-server/state.h" #include "scripting/scriptmanager.h" @@ -81,6 +82,10 @@ static void handleCraft(Character*, std::string&); static void handleGetPos(Character*, std::string&); static void handleSkills(Character*, std::string&); static void handleEffect(Character*, std::string&); +static void handleGiveSpecial(Character*, std::string&); +static void handleTakeSpecial(Character*, std::string&); +static void handleRechargeSpecial(Character*, std::string&); +static void handleListSpecials(Character*, std::string&); static CmdRef const cmdRef[] = { @@ -148,13 +153,27 @@ static CmdRef const cmdRef[] = "Shows an effect at the given position or on the given being. " "The player's character is targeted if neither of them is provided.", &handleEffect}, + {"givespecial", "<character> <special>", + "Gives the character the special. " + "The special can get passed as specialid or in the format " + "<setname>_<specialname>", &handleGiveSpecial}, + {"takespecial", "<character> <special>", + "Takes the special aways from the character. " + "The special can get passed as specialid or in the format " + "<setname>_<specialname>", &handleTakeSpecial}, + {"rechargespecial", "<character> <special>", + "Recharges the special of the character. " + "The special can get passed as specialid or in the format " + "<setname>_<specialname>", &handleRechargeSpecial}, + {"listspecials", "<character>", + "Lists the specials of the character.", &handleListSpecials}, {NULL, NULL, NULL, NULL} }; -static void say(const std::string error, Character *player) +static void say(const std::string message, Character *player) { - GameState::sayTo(player, NULL, error); + GameState::sayTo(player, NULL, message); } /* @@ -748,7 +767,7 @@ static void handleGoto(Character *player, std::string &args) other = gameHandler->getCharacterByNameSlow(character); if (!other) { - say("Invalid character, or they are offline.", player); + say("Invalid character, or player is offline.", player); return; } @@ -782,7 +801,7 @@ static void handleRecall(Character *player, std::string &args) other = gameHandler->getCharacterByNameSlow(character); if (!other) { - say("Invalid character, or they are offline.", player); + say("Invalid character, or player is offline.", player); return; } @@ -1413,7 +1432,7 @@ static void handleGetPos(Character *player, std::string &args) other = gameHandler->getCharacterByNameSlow(character); if (!other) { - say("Invalid character, or they are offline.", player); + say("Invalid character, or player is offline.", player); return; } const Point &pos = other->getPosition(); @@ -1446,7 +1465,7 @@ static void handleSkills(Character *player, std::string &args) other = gameHandler->getCharacterByNameSlow(character); if (!other) { - say("Invalid character, or they are offline.", player); + say("Invalid character, or player is offline.", player); return; } @@ -1509,6 +1528,175 @@ static void handleEffect(Character *player, std::string &args) } } +static void handleGiveSpecial(Character *player, std::string &args) +{ + std::string character = getArgument(args); + std::string special = getArgument(args); + if (character.empty() || special.empty()) + { + say("Invalid amount of arguments given.", player); + say("Usage: @givespecial <character> <special>", player); + return; + } + + Character *other; + if (character == "#") + other = player; + else + other = gameHandler->getCharacterByNameSlow(character); + + if (!other) + { + say("Invalid character, or player is offline.", player); + return; + } + + int specialId; + if (utils::isNumeric(special)) + specialId = utils::stringToInt(special); + else + specialId = specialManager->getId(special); + + if (specialId <= 0 || !other->giveSpecial(specialId)) + { + say("Invalid special.", player); + return; + } +} + +static void handleTakeSpecial(Character *player, std::string &args) +{ + std::string character = getArgument(args); + std::string special = getArgument(args); + if (character.empty() || special.empty()) + { + say("Invalid amount of arguments given.", player); + say("Usage: @takespecial <character> <special>", player); + return; + } + + Character *other; + if (character == "#") + other = player; + else + other = gameHandler->getCharacterByNameSlow(character); + + if (!other) + { + say("Invalid character, or player is offline.", player); + return; + } + + int specialId; + if (utils::isNumeric(special)) + specialId = utils::stringToInt(special); + else + specialId = specialManager->getId(special); + + if (specialId <= 0) + { + say("Invalid special.", player); + return; + } + if (!other->takeSpecial(specialId)) + { + say("Character does not have special.", player); + return; + } +} + +static void handleRechargeSpecial(Character *player, std::string &args) +{ + std::string character = getArgument(args); + std::string special = getArgument(args); + std::string newMana = getArgument(args); + if (character.empty() || special.empty()) + { + say("Invalid amount of arguments given.", player); + say("Usage: @rechargespecial <character> <special> [<mana>]", player); + return; + } + + Character *other; + if (character == "#") + other = player; + else + other = gameHandler->getCharacterByNameSlow(character); + + if (!other) + { + say("Invalid character, or player is offline.", player); + return; + } + + int specialId; + if (utils::isNumeric(special)) + specialId = utils::stringToInt(special); + else + specialId = specialManager->getId(special); + + SpecialManager::SpecialInfo *info = + specialManager->getSpecialInfo(specialId); + + if (!info) + { + say("Invalid special.", player); + return; + } + int mana; + if (newMana.empty()) + { + mana = info->neededMana; + } + else + { + if (!utils::isNumeric(newMana)) + { + say("Invalid mana amount given.", player); + return; + } + mana = utils::stringToInt(newMana); + } + if (!other->setSpecialMana(specialId, mana)) + { + say("Character does not have special.", player); + return; + } +} + +static void handleListSpecials(Character *player, std::string &args) +{ + std::string character = getArgument(args); + if (character.empty()) + { + say("Invalid amount of arguments given.", player); + say("Usage: @listspecials <character>", player); + return; + } + + Character *other; + if (character == "#") + other = player; + else + other = gameHandler->getCharacterByNameSlow(character); + + if (!other) + { + say("Invalid character, or player is offline.", player); + return; + } + + say("Specials of character " + other->getName() + ":", player); + for (SpecialMap::const_iterator it = other->getSpecialBegin(), + it_end = other->getSpecialEnd(); it != it_end; ++it) + { + const SpecialValue &info = it->second; + std::stringstream str; + str << info.specialInfo->id << ": " << info.specialInfo->setName << "/" + << info.specialInfo->name << " charge: " << info.currentMana; + say(str.str(), player); + } +} void CommandHandler::handleCommand(Character *player, const std::string &command) diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp index be8e2455..b65d64ad 100644 --- a/src/game-server/gamehandler.cpp +++ b/src/game-server/gamehandler.cpp @@ -147,6 +147,22 @@ static Actor *findActorNear(Actor *p, int id) return 0; } +static Being *findBeingNear(Actor *p, int id) +{ + MapComposite *map = p->getMap(); + const Point &ppos = p->getPosition(); + // See map.h for tiles constants + const int pixelDist = DEFAULT_TILE_LENGTH * TILES_TO_BE_NEAR; + for (BeingIterator i(map->getAroundPointIterator(ppos, pixelDist)); i; ++i) + { + Being *b = *i; + if (b->getPublicID() != id) + continue; + return ppos.inRangeOf(b->getPosition(), pixelDist) ? b : 0; + } + return 0; +} + static Character *findCharacterNear(Actor *p, int id) { MapComposite *map = p->getMap(); @@ -229,8 +245,12 @@ void GameHandler::processMessage(NetComputer *computer, MessageIn &message) handleAttack(client, message); break; - case PGMSG_USE_SPECIAL: - handleUseSpecial(client, message); + case PGMSG_USE_SPECIAL_ON_BEING: + handleUseSpecialOnBeing(client, message); + break; + + case PGMSG_USE_SPECIAL_ON_POINT: + handleUseSpecialOnPoint(client, message); break; case PGMSG_ACTION_CHANGE: @@ -615,21 +635,35 @@ void GameHandler::handleAttack(GameClient &client, MessageIn &message) LOG_DEBUG("Character " << client.character->getPublicID() << " attacked being " << id); - Actor *o = findActorNear(client.character, id); - if (o && o->getType() != OBJECT_NPC) + Being *being = findBeingNear(client.character, id); + if (being && being->getType() != OBJECT_NPC) { - Being *being = static_cast<Being*>(o); client.character->setTarget(being); client.character->setAction(ATTACK); } } -void GameHandler::handleUseSpecial(GameClient &client, MessageIn &message) +void GameHandler::handleUseSpecialOnBeing(GameClient &client, MessageIn &message) { const int specialID = message.readInt8(); + const int targetID = message.readInt16(); // 0 when no target is selected + Being *being = 0; + if (targetID != 0) + being = findBeingNear(client.character, targetID); + LOG_DEBUG("Character " << client.character->getPublicID() + << " tries to use his special attack " << specialID); + client.character->useSpecialOnBeing(specialID, being); +} + +void GameHandler::handleUseSpecialOnPoint(GameClient &client, MessageIn &message) +{ + const int specialID = message.readInt8(); + const int x = message.readInt16(); + const int y = message.readInt16(); + LOG_DEBUG("Character " << client.character->getPublicID() << " tries to use his special attack " << specialID); - client.character->useSpecial(specialID); + client.character->useSpecialOnPoint(specialID, x, y); } void GameHandler::handleActionChange(GameClient &client, MessageIn &message) diff --git a/src/game-server/gamehandler.h b/src/game-server/gamehandler.h index e60e478c..c0d843e9 100644 --- a/src/game-server/gamehandler.h +++ b/src/game-server/gamehandler.h @@ -133,7 +133,8 @@ class GameHandler: public ConnectionHandler void handleMoveItem(GameClient &client, MessageIn &message); void handleAttack(GameClient &client, MessageIn &message); - void handleUseSpecial(GameClient &client, MessageIn &message); + void handleUseSpecialOnBeing(GameClient &client, MessageIn &message); + void handleUseSpecialOnPoint(GameClient &client, MessageIn &message); void handleActionChange(GameClient &client, MessageIn &message); void handleDirectionChange(GameClient &client, MessageIn &message); diff --git a/src/game-server/main-game.cpp b/src/game-server/main-game.cpp index c3b3c36a..c7566b61 100644 --- a/src/game-server/main-game.cpp +++ b/src/game-server/main-game.cpp @@ -42,10 +42,11 @@ #include "game-server/accountconnection.h" #include "game-server/attributemanager.h" #include "game-server/gamehandler.h" -#include "game-server/skillmanager.h" #include "game-server/itemmanager.h" #include "game-server/mapmanager.h" #include "game-server/monstermanager.h" +#include "game-server/skillmanager.h" +#include "game-server/specialmanager.h" #include "game-server/statusmanager.h" #include "game-server/postman.h" #include "game-server/state.h" @@ -72,6 +73,7 @@ using utils::Logger; #define DEFAULT_STATUSDB_FILE "status-effects.xml" #define DEFAULT_PERMISSION_FILE "permissions.xml" #define DEFAULT_MAIN_SCRIPT_FILE "scripts/main.lua" +#define DEFAULT_SPECIALSDB_FILE "specials.xml" static int const WORLD_TICK_SKIP = 2; /** tolerance for lagging behind in world calculation) **/ @@ -86,6 +88,7 @@ AttributeManager *attributeManager = new AttributeManager(DEFAULT_ATTRIBUTEDB_FI ItemManager *itemManager = new ItemManager(DEFAULT_ITEMSDB_FILE, DEFAULT_EQUIPDB_FILE); MonsterManager *monsterManager = new MonsterManager(DEFAULT_MONSTERSDB_FILE); SkillManager *skillManager = new SkillManager(DEFAULT_SKILLSDB_FILE); +SpecialManager *specialManager = new SpecialManager(DEFAULT_SPECIALSDB_FILE); /** Core game message handler */ GameHandler *gameHandler; @@ -135,6 +138,7 @@ static void initializeServer() } attributeManager->initialize(); skillManager->initialize(); + specialManager->initialize(); itemManager->initialize(); monsterManager->initialize(); StatusManager::initialize(DEFAULT_STATUSDB_FILE); diff --git a/src/game-server/specialmanager.cpp b/src/game-server/specialmanager.cpp new file mode 100644 index 00000000..32669bca --- /dev/null +++ b/src/game-server/specialmanager.cpp @@ -0,0 +1,177 @@ +/* + * The Mana Server + * Copyright (C) 2004-2010 The Mana World Development Team + * Copyright (C) 2010-2012 The Mana Developers + * + * This file is part of The Mana Server. + * + * The Mana Server is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "specialmanager.h" + +#include "utils/xml.h" +#include "utils/logger.h" + +static SpecialManager::TargetMode getTargetByString(const std::string &str) +{ + std::string strLower = utils::toLower(str); + if (strLower == "being") + return SpecialManager::TARGET_BEING; + else if (strLower == "point") + return SpecialManager::TARGET_POINT; + + LOG_WARN("Unknown targetmode " << str << " assuming being."); + return SpecialManager::TARGET_BEING; +} + +void SpecialManager::readSpecialNode(xmlNodePtr specialNode, + const std::string &setName) +{ + std::string name = utils::toLower( + XML::getProperty(specialNode, "name", std::string())); + int id = XML::getProperty(specialNode, "id", 0); + + if (id <= 0 || name.empty()) + { + LOG_WARN("Invalid special (empty name or id <= 0) in set: " << setName); + return; + } + + SpecialsInfo::iterator it = mSpecialsInfo.find(id); + if (it != mSpecialsInfo.end()) + { + LOG_WARN("SpecialManager: The same id: " << id + << " is given for special names: " << it->first + << " and " << name); + LOG_WARN("The special reference: " << id + << ": '" << name << "' will be ignored."); + return; + } + + bool rechargeable = XML::getBoolProperty(specialNode, "rechargeable", true); + int neededMana = XML::getProperty(specialNode, "needed", 0); + int defaultRechargeSpeed = XML::getProperty(specialNode, + "rechargespeed", 0); + + if (rechargeable && neededMana <= 0) + { + LOG_WARN("Invalid special '" << name + << "' (rechargable but no needed attribute) in set: " + << setName); + return; + } + + + SpecialInfo *newInfo = new SpecialManager::SpecialInfo; + newInfo->setName = setName; + newInfo->name = name; + newInfo->id = id; + newInfo->rechargeable = rechargeable; + newInfo->neededMana = neededMana; + newInfo->defaultRechargeSpeed = defaultRechargeSpeed; + + newInfo->target = getTargetByString(XML::getProperty(specialNode, "target", + std::string())); + + mSpecialsInfo[newInfo->id] = newInfo; + + std::string keyName = setName + "_" + newInfo->name; + mNamedSpecialsInfo[keyName] = newInfo; +} + +void SpecialManager::initialize() +{ + clear(); + + XML::Document doc(mSpecialFile); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "specials")) + { + LOG_ERROR("Special Manager: " << mSpecialFile + << " is not a valid database file!"); + return; + } + + LOG_INFO("Loading special reference: " << mSpecialFile); + + for_each_xml_child_node(setNode, rootNode) + { + // The server will prefix the core name with the set, so we need one. + if (!xmlStrEqual(setNode->name, BAD_CAST "set")) + continue; + + std::string setName = XML::getProperty(setNode, "name", std::string()); + if (setName.empty()) + { + LOG_WARN("The " << mSpecialFile << " file is containing unamed <set> " + "tags and will be ignored."); + continue; + } + + setName = utils::toLower(setName); + + for_each_xml_child_node(specialNode, setNode) + { + if (!xmlStrEqual(specialNode->name, BAD_CAST "special")) + continue; + readSpecialNode(specialNode, setName); + } + } +} + +void SpecialManager::clear() +{ + for (SpecialsInfo::iterator it = mSpecialsInfo.begin(), + it_end = mSpecialsInfo.end(); it != it_end; ++it) + { + delete it->second; + } + mSpecialsInfo.clear(); + mNamedSpecialsInfo.clear(); +} + +unsigned int SpecialManager::getId(const std::string &set, + const std::string &name) const +{ + std::string key = utils::toLower(set) + "_" + utils::toLower(name); + return getId(key); +} + +unsigned int SpecialManager::getId(const std::string &specialName) const +{ + if (mNamedSpecialsInfo.contains(specialName)) + return mNamedSpecialsInfo.value(specialName)->id; + else + return 0; +} + +const std::string SpecialManager::getSpecialName(int id) const +{ + SpecialsInfo::const_iterator it = mSpecialsInfo.find(id); + return it != mSpecialsInfo.end() ? it->second->name : ""; +} + +const std::string SpecialManager::getSetName(int id) const +{ + SpecialsInfo::const_iterator it = mSpecialsInfo.find(id); + return it != mSpecialsInfo.end() ? it->second->setName : ""; +} + +SpecialManager::SpecialInfo *SpecialManager::getSpecialInfo(int id) +{ + SpecialsInfo::const_iterator it = mSpecialsInfo.find(id); + return it != mSpecialsInfo.end() ? it->second : 0; +} diff --git a/src/game-server/specialmanager.h b/src/game-server/specialmanager.h new file mode 100644 index 00000000..b7a4f54b --- /dev/null +++ b/src/game-server/specialmanager.h @@ -0,0 +1,114 @@ +/* + * The Mana Server + * Copyright (C) 2004-2010 The Mana World Development Team + * + * This file is part of The Mana Server. + * + * The Mana Server is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef SPECIALMANAGER_H +#define SPECIALMANAGER_H + +#include "utils/string.h" +#include "utils/xml.h" + +#include "scripting/script.h" + + + +class SpecialManager +{ +public: + enum TargetMode + { + TARGET_BEING, + TARGET_POINT + }; + + struct SpecialInfo + { + SpecialInfo() : + id(0), + rechargeable(false), + defaultRechargeSpeed(0), + neededMana(0), + target(TARGET_BEING) + {} + + unsigned int id; + std::string name; + std::string setName; + bool rechargeable; + int defaultRechargeSpeed; + unsigned int neededMana; + TargetMode target; + Script::Ref rechargedCallback; + Script::Ref useCallback; + }; + + SpecialManager(const std::string &specialFile): + mSpecialFile(specialFile) + { } + + ~SpecialManager() + { clear(); } + + /** + * Loads special reference file. + */ + void initialize(); + + /** + * Reloads special reference file. + */ + void reload(); + + /** + * Gets the specials Id from a set and a special string. + */ + unsigned int getId(const std::string &set, const std::string &name) const; + + /** + * Gets the specials Id from a string formatted in this way: + * "setname_skillname" + */ + unsigned int getId(const std::string &specialName) const; + + const std::string getSpecialName(int id) const; + const std::string getSetName(int id) const; + + SpecialInfo *getSpecialInfo(int id); + +private: + /** + * Clears up the special maps. + */ + void clear(); + + void readSpecialNode(xmlNodePtr skillNode, + const std::string &setName); + + std::string mSpecialFile; + typedef std::map<unsigned int, SpecialInfo*> SpecialsInfo; + SpecialsInfo mSpecialsInfo; + typedef utils::NameMap<SpecialInfo*> NamedSpecialsInfo; + NamedSpecialsInfo mNamedSpecialsInfo; + +}; + +extern SpecialManager *specialManager; + +#endif // SPECIALMANAGER_H |