summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorErik Schilling <ablu.erikschilling@googlemail.com>2012-04-03 13:29:05 +0200
committerErik Schilling <ablu.erikschilling@googlemail.com>2012-04-04 16:22:11 +0200
commitf8e816d9185c09d1c17d921b775e483d132982e5 (patch)
tree3ab299ab6057db3bfd8feb24130a6fcb7e64a60d /src
parente4baa92aae537921dd17873328a95ab17afcfdfc (diff)
downloadmanaserv-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')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/account-server/character.cpp8
-rw-r--r--src/account-server/character.h31
-rw-r--r--src/account-server/storage.cpp23
-rw-r--r--src/common/manaserv_protocol.h5
-rw-r--r--src/game-server/character.cpp203
-rw-r--r--src/game-server/character.h63
-rw-r--r--src/game-server/commandhandler.cpp200
-rw-r--r--src/game-server/gamehandler.cpp48
-rw-r--r--src/game-server/gamehandler.h3
-rw-r--r--src/game-server/main-game.cpp6
-rw-r--r--src/game-server/specialmanager.cpp177
-rw-r--r--src/game-server/specialmanager.h114
-rw-r--r--src/scripting/lua.cpp140
-rw-r--r--src/scripting/luascript.cpp5
-rw-r--r--src/scripting/luautil.cpp23
-rw-r--r--src/scripting/luautil.h4
-rw-r--r--src/scripting/scriptmanager.cpp33
-rw-r--r--src/scripting/scriptmanager.h3
-rw-r--r--src/serialize/characterdata.h7
-rw-r--r--src/sql/mysql/createTables.sql7
-rw-r--r--src/sql/sqlite/createTables.sql7
-rw-r--r--src/sql/sqlite/updates/update_20_to_21.sql11
23 files changed, 951 insertions, 172 deletions
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 *>(&registryKey));
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;