diff options
26 files changed, 988 insertions, 199 deletions
diff --git a/example/permissions.xml b/example/permissions.xml index e6615864..4c46af1a 100644 --- a/example/permissions.xml +++ b/example/permissions.xml @@ -25,6 +25,10 @@ <allow>@killmonsters</allow> <allow>@getpos</allow> <allow>@effect</allow> + <allow>@givespecial</allow> + <allow>@takespecial</allow> + <allow>@rechargespecial</allow> + <allow>@listspecials</allow> </class> <class level="4"> <alias>gm</alias> diff --git a/example/scripts/special_actions.lua b/example/scripts/special_actions.lua index 25619f41..6070ecd3 100644 --- a/example/scripts/special_actions.lua +++ b/example/scripts/special_actions.lua @@ -8,28 +8,15 @@ --]] -local specialCost = {} -specialCost[1] = 50 -specialCost[2] = 250 -specialCost[3] = 1000 +local spell1 = get_special_info("Magic_Test Spell 1") +spell1:on_use(function(user, target, specialid) + target = target or user + being_say(target, "Kaaame...Haaame... HAAAAAA!") + chr_set_special_mana(user, specialid, 0) +end) +spell1:on_recharged(function(ch) being_say(ch, "Hoooooooo...") end) -local function use_special(ch, id) - -- perform whatever the special with the ID does - if id == 1 then - being_say(ch, "Kaaame...Haaame... HAAAAAA!") - end - if id == 2 then - being_say(ch, "HAA-DOKEN!") - end - if id == 3 then - being_say(ch, "Sonic BOOM") - end -end +local spell2 = get_special_info(2) +spell2:on_use(function(user) being_say(user, "HAA-DOKEN!") end) -local function get_special_recharge_cost(id) - -- return the recharge cost for the special with the ID - return specialCost[id] -end - -on_use_special(use_special) -on_get_special_recharge_cost(get_special_recharge_cost) +get_special_info(3):on_use(function(user) being_say(user, "Sonic BOOM") end) diff --git a/example/specials.xml b/example/specials.xml index c234f667..2c1d61a6 100644 --- a/example/specials.xml +++ b/example/specials.xml @@ -1,9 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> <specials> <set name="Magic"> - <special id="1" name="Test Spell 1" active="true" recharge="true" /> - <special id="2" name="Test Spell 2" active="true" recharge="true" /> - <special id="3" name="Test Spell 3" active="true" recharge="true" /> - <special id="4" name="Passive Special 1" active="false" /> + <special + id="1" + name="Test Spell 1" + rechargeable="true" + needed="100" + rechargespeed="10" + /> + <special + id="2" + name="Test Spell 2" + rechargeable="true" + needed="1000" + rechargespeed="10" + target="being" + /> + <special + id="3" + name="Test Spell 3" + rechargeable="true" + needed="10000" + rechargespeed="10" + target="point" + /> </set> </specials> diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ecefab4f..34ec3891 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -242,6 +242,8 @@ SET(SRCS_MANASERVGAME game-server/skillmanager.cpp game-server/spawnarea.h game-server/spawnarea.cpp + game-server/specialmanager.cpp + game-server/specialmanager.h game-server/state.h game-server/state.cpp game-server/statuseffect.h diff --git a/src/account-server/character.cpp b/src/account-server/character.cpp index 535ee67b..3219e3ce 100644 --- a/src/account-server/character.cpp +++ b/src/account-server/character.cpp @@ -45,3 +45,11 @@ void Character::setAccount(Account *acc) mAccountID = acc->getID(); mAccountLevel = acc->getLevel(); } + +void Character::giveSpecial(int id, int currentMana) +{ + if (mSpecials.find(id) == mSpecials.end()) + { + mSpecials[id] = SpecialValue(currentMana); + } +} diff --git a/src/account-server/character.h b/src/account-server/character.h index 74fe923c..5e6bd3b3 100644 --- a/src/account-server/character.h +++ b/src/account-server/character.h @@ -49,13 +49,28 @@ struct AttributeValue double modified; /**< Value after various modifiers have been applied. */ }; +struct SpecialValue +{ + SpecialValue() + : currentMana(0) + {} + + SpecialValue(unsigned int currentMana) + : currentMana(currentMana) + {} + + unsigned int currentMana; +}; + /** * Stores attributes by their id. */ -typedef std::map< unsigned int, AttributeValue > AttributeMap; +typedef std::map<unsigned int, AttributeValue> AttributeMap; -/** placeholder type needed for include compatibility with game server*/ -typedef void Special; +/** + * Stores specials by their id. + */ +typedef std::map<unsigned int, SpecialValue> SpecialMap; class Character { @@ -193,17 +208,17 @@ class Character int getSpecialSize() const { return mSpecials.size(); } - const std::map<int, Special*>::const_iterator getSpecialBegin() const + SpecialMap::const_iterator getSpecialBegin() const { return mSpecials.begin(); } - const std::map<int, Special*>::const_iterator getSpecialEnd() const + SpecialMap::const_iterator getSpecialEnd() const { return mSpecials.end(); } + void clearSpecials() { mSpecials.clear(); } - void giveSpecial(int id) - { mSpecials[id] = NULL; } + void giveSpecial(int id, int currentMana); /** * Gets the Id of the map that the character is on. @@ -270,7 +285,7 @@ class Character std::map<int, int> mExperience; //!< Skill Experience. std::map<int, int> mStatusEffects; //!< Status Effects std::map<int, int> mKillCount; //!< Kill Count - std::map<int, Special*> mSpecials; + SpecialMap mSpecials; unsigned short mMapId; //!< Map the being is on. unsigned char mGender; //!< Gender of the being. unsigned char mHairStyle; //!< Hair style of the being. diff --git a/src/account-server/storage.cpp b/src/account-server/storage.cpp index 9a19a673..8876cd4b 100644 --- a/src/account-server/storage.cpp +++ b/src/account-server/storage.cpp @@ -24,6 +24,7 @@ #include "account-server/storage.h" #include "account-server/account.h" +#include "account-server/character.h" #include "account-server/flooritem.h" #include "chat-server/chatchannel.h" #include "chat-server/guild.h" @@ -483,14 +484,18 @@ Character *Storage::getCharacterBySQL(Account *owner) // Load the special status s.clear(); s.str(""); - s << "select special_id FROM " << CHAR_SPECIALS_TBL_NAME + s << "SELECT special_id, special_current_mana FROM " + << CHAR_SPECIALS_TBL_NAME << " WHERE char_id = " << character->getDatabaseID(); const dal::RecordSet &specialsInfo = mDb->execSql(s.str()); if (!specialsInfo.isEmpty()) { const unsigned int nRows = specialsInfo.rows(); for (unsigned int row = 0; row < nRows; row++) - character->giveSpecial(toUint(specialsInfo(row, 0))); + { + character->giveSpecial(toUint(specialsInfo(row, 0)), + toUint(specialsInfo(row, 1))); + } } } catch (const dal::DbSqlQueryExecFailure &e) @@ -777,15 +782,19 @@ bool Storage::updateCharacter(Character *character) << character->getDatabaseID() << "';"; mDb->execSql(deleteSql.str()); // In with the new - std::map<int, Special*>::const_iterator special_it; - for (special_it = character->getSpecialBegin(); - special_it != character->getSpecialEnd(); special_it++) + SpecialMap::const_iterator special_it, special_it_end; + for (special_it = character->getSpecialBegin(), + special_it_end = character->getSpecialEnd(); + special_it != special_it_end; ++special_it) { insertSql.str(""); insertSql << "INSERT INTO " << CHAR_SPECIALS_TBL_NAME - << " (char_id, special_id) VALUES (" + << " (char_id, special_id, special_current_mana)" + << " VALUES (" << " '" << character->getDatabaseID() << "'," - << " '" << special_it->first << "');"; + << " '" << special_it->first << "'," + << " '" << special_it->second.currentMana + << "');"; mDb->execSql(insertSql.str()); } } diff --git a/src/common/manaserv_protocol.h b/src/common/manaserv_protocol.h index 1ead90e9..0a61dcb5 100644 --- a/src/common/manaserv_protocol.h +++ b/src/common/manaserv_protocol.h @@ -30,7 +30,7 @@ namespace ManaServ { enum { PROTOCOL_VERSION = 1, - SUPPORTED_DB_VERSION = 20 + SUPPORTED_DB_VERSION = 21 }; /** @@ -131,8 +131,9 @@ enum { GPMSG_ITEMS = 0x0281, // { W item id, W*2 position }* PGMSG_ATTACK = 0x0290, // W being id GPMSG_BEING_ATTACK = 0x0291, // W being id, B direction, B attack Id - PGMSG_USE_SPECIAL = 0x0292, // B specialID + PGMSG_USE_SPECIAL_ON_BEING = 0x0292, // B specialID, W being id GPMSG_SPECIAL_STATUS = 0x0293, // { B specialID, D current, D max, D recharge } + PGMSG_USE_SPECIAL_ON_POINT = 0x0294, // B specialID, W*2 position PGMSG_SAY = 0x02A0, // S text GPMSG_SAY = 0x02A1, // W being id, S text GPMSG_NPC_CHOICE = 0x02B0, // W being id, { S text }* 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 diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 2d98c713..47672659 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -1819,9 +1819,10 @@ static int chr_give_special(lua_State *s) { // cost_type is ignored until we have more than one cost type Character *c = checkCharacter(s, 1); - const int special = luaL_checkint(s, 2); + const int special = checkSpecial(s, 2); + const int currentMana = luaL_optint(s, 3, 0); - c->giveSpecial(special); + c->giveSpecial(special, currentMana); return 0; } @@ -1853,6 +1854,70 @@ static int chr_take_special(lua_State *s) } /** + * chr_set_special_recharge_speed(Character*, int special, int speed) + * Sets recharge speed for a special. + */ +static int chr_set_special_recharge_speed(lua_State *s) +{ + Character *c = checkCharacter(s, 1); + const int special = checkSpecial(s, 2); + const int speed = luaL_checkint(s, 3); + + if (c->setSpecialRechargeSpeed(special, speed)) + raiseScriptError(s, "chr_set_special_mana called with special " + "that is not owned by character."); + return 0; +} + +/** + * chr_get_special_recharge_speed(Character*, int special) + * Gets recharge speed of a special. + */ +static int chr_get_special_recharge_speed(lua_State *s) +{ + Character *c = checkCharacter(s, 1); + const int special = checkSpecial(s, 2); + + SpecialMap::iterator it = c->findSpecial(special); + + luaL_argcheck(s, it != c->getSpecialEnd(), 2, + "character does not have special"); + + lua_pushinteger(s, it->second.rechargeSpeed); + return 1; +} + +/** + * chr_set_special_mana(Character*, int special, int mana) + * Sets the current charge of the special. + */ +static int chr_set_special_mana(lua_State *s) +{ + Character *c = checkCharacter(s, 1); + const int special = checkSpecial(s, 2); + const int mana = luaL_checkint(s, 3); + if (!c->setSpecialMana(special, mana)) + raiseScriptError(s, "chr_set_special_mana called with special " + "that is not owned by character."); + return 0; +} + +/** + * chr_get_special_mana(Character*, int special): int + * Gets the current charge of a special. + */ +static int chr_get_special_mana(lua_State *s) +{ + Character *c = checkCharacter(s, 1); + const int special = checkSpecial(s, 2); + SpecialMap::iterator it = c->findSpecial(special); + luaL_argcheck(s, it != c->getSpecialEnd(), 2, + "character does not have special"); + lua_pushinteger(s, it->second.currentMana); + return 1; +} + +/** * chr_get_rights(Character*): int * Returns the rights level of a character. */ @@ -2207,6 +2272,61 @@ static int announce(lua_State *s) return 0; } +static int get_special_info(lua_State *s) +{ + const int special = checkSpecial(s, 1); + SpecialManager::SpecialInfo *info = specialManager->getSpecialInfo(special); + luaL_argcheck(s, info, 1, "invalid special"); + LuaSpecialInfo::push(s, info); + return 1; +} + +static int specialinfo_get_name(lua_State *s) +{ + SpecialManager::SpecialInfo *info = LuaSpecialInfo::check(s, 1); + lua_pushstring(s, info->name.c_str()); + return 1; +} + +static int specialinfo_get_needed_mana(lua_State *s) +{ + SpecialManager::SpecialInfo *info = LuaSpecialInfo::check(s, 1); + lua_pushinteger(s, info->neededMana); + return 1; +} + +static int specialinfo_is_rechargeable(lua_State *s) +{ + SpecialManager::SpecialInfo *info = LuaSpecialInfo::check(s, 1); + lua_pushboolean(s, info->rechargeable); + return 1; +} + +static int specialinfo_get_category(lua_State *s) +{ + SpecialManager::SpecialInfo *info = LuaSpecialInfo::check(s, 1); + lua_pushstring(s, info->setName.c_str()); + return 1; +} + +static int specialinfo_on_recharged(lua_State *s) +{ + SpecialManager::SpecialInfo *info = LuaSpecialInfo::check(s, 1); + Script *script = getScript(s); + luaL_checktype(s, 2, LUA_TFUNCTION); + script->assignCallback(info->rechargedCallback); + return 0; +} + +static int specialinfo_on_use(lua_State *s) +{ + SpecialManager::SpecialInfo *info = LuaSpecialInfo::check(s, 1); + Script *script = getScript(s); + luaL_checktype(s, 2, LUA_TFUNCTION); + script->assignCallback(info->useCallback); + return 0; +} + static int require_loader(lua_State *s) { // Add .lua extension (maybe only do this when it doesn't have it already) @@ -2292,6 +2412,10 @@ LuaScript::LuaScript(): { "chr_give_special", &chr_give_special }, { "chr_has_special", &chr_has_special }, { "chr_take_special", &chr_take_special }, + { "chr_set_special_recharge_speed", &chr_set_special_recharge_speed }, + { "chr_get_special_recharge_speed", &chr_get_special_recharge_speed }, + { "chr_set_special_mana", &chr_set_special_mana }, + { "chr_get_special_mana", &chr_get_special_mana }, { "chr_kick", &chr_kick }, { "exp_for_level", &exp_for_level }, { "monster_create", &monster_create }, @@ -2346,6 +2470,7 @@ LuaScript::LuaScript(): { "get_distance", &get_distance }, { "map_get_objects", &map_get_objects }, { "announce", &announce }, + { "get_special_info", &get_special_info }, { NULL, NULL } }; lua_pushvalue(mRootState, LUA_GLOBALSINDEX); @@ -2376,10 +2501,21 @@ LuaScript::LuaScript(): { NULL, NULL } }; + static luaL_Reg const members_SpecialInfo[] = { + { "name", &specialinfo_get_name }, + { "needed_mana", &specialinfo_get_needed_mana }, + { "rechargeable", &specialinfo_is_rechargeable }, + { "on_use", &specialinfo_on_use }, + { "on_recharged", &specialinfo_on_recharged }, + { "category", &specialinfo_get_category }, + { NULL, NULL} + }; + LuaItemClass::registerType(mRootState, "ItemClass", members_ItemClass); LuaMapObject::registerType(mRootState, "MapObject", members_MapObject); LuaMonsterClass::registerType(mRootState, "MonsterClass", members_MonsterClass); LuaStatusEffect::registerType(mRootState, "StatusEffect", members_StatusEffect); + LuaSpecialInfo::registerType(mRootState, "SpecialInfo", members_SpecialInfo); // Make script object available to callback functions. lua_pushlightuserdata(mRootState, const_cast<char *>(®istryKey)); diff --git a/src/scripting/luascript.cpp b/src/scripting/luascript.cpp index 36adb912..3a45b3fc 100644 --- a/src/scripting/luascript.cpp +++ b/src/scripting/luascript.cpp @@ -89,7 +89,10 @@ void LuaScript::push(const std::string &v) void LuaScript::push(Thing *v) { assert(nbArgs >= 0); - lua_pushlightuserdata(mCurrentState, v); + if (v) + lua_pushlightuserdata(mCurrentState, v); + else + lua_pushnil(mCurrentState); ++nbArgs; } diff --git a/src/scripting/luautil.cpp b/src/scripting/luautil.cpp index 67dd7395..feed7568 100644 --- a/src/scripting/luautil.cpp +++ b/src/scripting/luautil.cpp @@ -255,13 +255,22 @@ NPC *checkNPC(lua_State *s, int p) int checkSkill(lua_State *s, int p) { - if (lua_isstring(s, p)) - { - int id = skillManager->getId(luaL_checkstring(s, p)); - luaL_argcheck(s, id != 0, p, "invalid skill name"); - return id; - } - return luaL_checkint(s, 2); + if (lua_isnumber(s, p)) + return luaL_checkint(s, p); + + int id = skillManager->getId(luaL_checkstring(s, p)); + luaL_argcheck(s, id != 0, p, "invalid special name"); + return id; +} + +int checkSpecial(lua_State *s, int p) +{ + if (lua_isnumber(s, p)) + return luaL_checkint(s, p); + + int id = specialManager->getId(luaL_checkstring(s, p)); + luaL_argcheck(s, id != 0, p, "invalid special name"); + return id; } diff --git a/src/scripting/luautil.h b/src/scripting/luautil.h index a0eac120..86a748a4 100644 --- a/src/scripting/luautil.h +++ b/src/scripting/luautil.h @@ -34,6 +34,8 @@ extern "C" { #include <set> #include <vector> +#include "game-server/specialmanager.h" + class Being; class Character; class ItemClass; @@ -152,6 +154,7 @@ typedef LuaUserData<ItemClass> LuaItemClass; typedef LuaUserData<MapObject> LuaMapObject; typedef LuaUserData<MonsterClass> LuaMonsterClass; typedef LuaUserData<StatusEffect> LuaStatusEffect; +typedef LuaUserData<SpecialManager::SpecialInfo> LuaSpecialInfo; Script * getScript(lua_State *s); @@ -169,6 +172,7 @@ Monster * checkMonster(lua_State *s, int p); MonsterClass * checkMonsterClass(lua_State *s, int p); NPC * checkNPC(lua_State *s, int p); int checkSkill(lua_State *s, int p); +int checkSpecial(lua_State *s, int p); MapComposite * checkCurrentMap(lua_State *s, Script *script = 0); Script::Thread* checkCurrentThread(lua_State *s, Script *script = 0); diff --git a/src/scripting/scriptmanager.cpp b/src/scripting/scriptmanager.cpp index c133a5e2..76c4dae9 100644 --- a/src/scripting/scriptmanager.cpp +++ b/src/scripting/scriptmanager.cpp @@ -51,39 +51,6 @@ Script *ScriptManager::currentState() return _currentState; } -// TODO: Have some generic event mechanism rather than calling global functions - -void ScriptManager::addDataToSpecial(int id, Special *special) -{ - /* currently only gets the recharge cost. - TODO: get any other info in a similar way, but - first we have to agree on what other - info we actually want to provide. - */ - if (special && _getSpecialRechargeCostCallback.isValid()) - { - _currentState->prepare(_getSpecialRechargeCostCallback); - _currentState->push(id); - int scriptReturn = _currentState->execute(); - special->neededMana = scriptReturn; - } -} - -bool ScriptManager::performSpecialAction(int specialId, Being *caster) -{ - if (!_specialCallback.isValid()) - { - LOG_WARN("No callback for specials set! Specials disabled."); - return false; - } - - _currentState->prepare(_specialCallback); - _currentState->push(caster); - _currentState->push(specialId); - _currentState->execute(); - return true; -} - bool ScriptManager::performCraft(Being *crafter, const std::list<InventoryItem> &recipe) { diff --git a/src/scripting/scriptmanager.h b/src/scripting/scriptmanager.h index d0d03707..03b6e64b 100644 --- a/src/scripting/scriptmanager.h +++ b/src/scripting/scriptmanager.h @@ -26,7 +26,6 @@ #include <string> class Script; -class Special; /** * Manages the script states. In fact at the moment it simply provides access @@ -56,8 +55,6 @@ bool loadMainScript(const std::string &file); */ Script *currentState(); -void addDataToSpecial(int specialId, Special *special); -bool performSpecialAction(int specialId, Being *caster); bool performCraft(Being *crafter, const std::list<InventoryItem> &recipe); void setCraftCallback(Script *script); diff --git a/src/serialize/characterdata.h b/src/serialize/characterdata.h index 14a7097b..cab51197 100644 --- a/src/serialize/characterdata.h +++ b/src/serialize/characterdata.h @@ -89,11 +89,12 @@ void serializeCharacterData(const T &data, MessageOut &msg) } // character specials - std::map<int, Special*>::const_iterator special_it; + SpecialMap::const_iterator special_it; msg.writeInt16(data.getSpecialSize()); for (special_it = data.getSpecialBegin(); special_it != data.getSpecialEnd() ; special_it++) { msg.writeInt32(special_it->first); + msg.writeInt32(special_it->second.currentMana); } // inventory - must be last because size isn't transmitted @@ -183,7 +184,9 @@ void deserializeCharacterData(T &data, MessageIn &msg) data.clearSpecials(); for (int i = 0; i < specialSize; i++) { - data.giveSpecial(msg.readInt32()); + const int id = msg.readInt32(); + const int mana = msg.readInt32(); + data.giveSpecial(id, mana); } diff --git a/src/sql/mysql/createTables.sql b/src/sql/mysql/createTables.sql index 4917ef47..c47cbd1e 100644 --- a/src/sql/mysql/createTables.sql +++ b/src/sql/mysql/createTables.sql @@ -117,8 +117,9 @@ DEFAULT CHARSET=utf8; CREATE TABLE mana_char_specials ( - `char_id` int(10) unsigned NOT NULL, - `special_id` int(10) unsigned NOT NULL, + `char_id` int(10) unsigned NOT NULL, + `special_id` int(10) unsigned NOT NULL, + `special_current_mana` int(10) unsigned NOT NULL, PRIMARY KEY (`char_id`, `special_id`), FOREIGN KEY (`char_id`) REFERENCES `mana_characters` (`id`) @@ -437,7 +438,7 @@ AUTO_INCREMENT=0 ; INSERT INTO mana_world_states VALUES('accountserver_startup',-1,'0', NOW()); INSERT INTO mana_world_states VALUES('accountserver_version',-1,'0', NOW()); -INSERT INTO mana_world_states VALUES('database_version', -1,'20', NOW()); +INSERT INTO mana_world_states VALUES('database_version', -1,'21', NOW()); -- all known transaction codes diff --git a/src/sql/sqlite/createTables.sql b/src/sql/sqlite/createTables.sql index b286467e..06dbd195 100644 --- a/src/sql/sqlite/createTables.sql +++ b/src/sql/sqlite/createTables.sql @@ -120,8 +120,9 @@ CREATE INDEX mana_char_kill_stats_char on mana_char_kill_stats ( char_id ); CREATE TABLE mana_char_specials ( - char_id INTEGER NOT NULL, - special_id INTEGER NOT NULL, + char_id INTEGER NOT NULL, + special_id INTEGER NOT NULL, + special_current_mana INTEGER NOT NULL, PRIMARY KEY (char_id, special_id), FOREIGN KEY (char_id) REFERENCES mana_characters(id) ); @@ -421,7 +422,7 @@ AS INSERT INTO mana_world_states VALUES('accountserver_startup',-1,'0', strftime('%s','now')); INSERT INTO mana_world_states VALUES('accountserver_version',-1,'0', strftime('%s','now')); -INSERT INTO mana_world_states VALUES('database_version', -1,'20', strftime('%s','now')); +INSERT INTO mana_world_states VALUES('database_version', -1,'21', strftime('%s','now')); -- all known transaction codes diff --git a/src/sql/sqlite/updates/update_20_to_21.sql b/src/sql/sqlite/updates/update_20_to_21.sql new file mode 100644 index 00000000..84565326 --- /dev/null +++ b/src/sql/sqlite/updates/update_20_to_21.sql @@ -0,0 +1,11 @@ +BEGIN; + +ALTER TABLE mana_char_specials ADD COLUMN special_current_mana INTEGER DEFAULT 0 NOT NULL; + +-- Update the database version, and set date of update +UPDATE mana_world_states + SET value = '21', + moddate = strftime('%s','now') + WHERE state_name = 'database_version'; + +END; |