diff options
Diffstat (limited to 'src')
82 files changed, 4155 insertions, 3303 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 629df5ee..85d6b6d2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -41,6 +41,8 @@ SET(FLAGS "${FLAGS} -DPACKAGE_VERSION=\\\"${VERSION}\\\"") SET(FLAGS "${FLAGS} -DPKG_DATADIR=\\\"${PKG_DATADIR}/\\\"") SET(FLAGS "${FLAGS} -DLOCALEDIR=\\\"${LOCALEDIR}/\\\"") +SET(FLAGS "${FLAGS} -std=c++0x") + # If the Sqlite option is enabled... IF (WITH_SQLITE) FIND_PACKAGE(Sqlite3 REQUIRED) @@ -212,9 +214,11 @@ SET(SRCS_MANASERVGAME game-server/character.cpp game-server/collisiondetection.h game-server/collisiondetection.cpp - game-server/command.cpp + game-server/combatcomponent.h + game-server/combatcomponent.cpp game-server/commandhandler.cpp game-server/commandhandler.h + game-server/component.h game-server/effect.h game-server/effect.cpp game-server/emotemanager.h @@ -239,6 +243,8 @@ SET(SRCS_MANASERVGAME game-server/mapreader.cpp game-server/monster.h game-server/monster.cpp + game-server/monstercombatcomponent.h + game-server/monstercombatcomponent.cpp game-server/monstermanager.h game-server/monstermanager.cpp game-server/npc.h @@ -248,8 +254,8 @@ SET(SRCS_MANASERVGAME game-server/quest.cpp game-server/skillmanager.h game-server/skillmanager.cpp - game-server/spawnarea.h - game-server/spawnarea.cpp + game-server/spawnareacomponent.h + game-server/spawnareacomponent.cpp game-server/specialmanager.cpp game-server/specialmanager.h game-server/state.h @@ -262,8 +268,8 @@ SET(SRCS_MANASERVGAME game-server/timeout.cpp game-server/trade.h game-server/trade.cpp - game-server/trigger.h - game-server/trigger.cpp + game-server/triggerareacomponent.h + game-server/triggerareacomponent.cpp scripting/script.h scripting/script.cpp scripting/scriptmanager.h diff --git a/src/account-server/account.cpp b/src/account-server/account.cpp index ed7c5df4..10869294 100644 --- a/src/account-server/account.cpp +++ b/src/account-server/account.cpp @@ -41,7 +41,7 @@ void Account::setCharacters(const Characters &characters) mCharacters = characters; } -void Account::addCharacter(Character *character) +void Account::addCharacter(CharacterData *character) { unsigned slot = (unsigned) character->getCharacterSlot(); assert(isSlotEmpty(slot)); diff --git a/src/account-server/account.h b/src/account-server/account.h index 46693e80..0ee84768 100644 --- a/src/account-server/account.h +++ b/src/account-server/account.h @@ -151,7 +151,7 @@ class Account * * @param character the new character. */ - void addCharacter(Character *character); + void addCharacter(CharacterData *character); /** * Removes a character from the account. diff --git a/src/account-server/accounthandler.cpp b/src/account-server/accounthandler.cpp index a65d8240..41b2c034 100644 --- a/src/account-server/accounthandler.cpp +++ b/src/account-server/accounthandler.cpp @@ -76,7 +76,8 @@ public: /** * Send the character data to the client. */ - static void sendCharacterData(AccountClient &client, const Character &ch); + static void sendCharacterData(AccountClient &client, + const CharacterData &ch); protected: /** @@ -271,7 +272,7 @@ void AccountHandler::computerDisconnected(NetComputer *comp) } void AccountHandler::sendCharacterData(AccountClient &client, - const Character &ch) + const CharacterData &ch) { MessageOut charInfo(APMSG_CHAR_INFO); charInfo.writeInt8(ch.getCharacterSlot()); @@ -760,7 +761,7 @@ void AccountHandler::handleCharacterCreateMessage(AccountClient &client, for (unsigned i = 0; i < mModifiableAttributes.size(); ++i) attributes[i] = 5; - Character *newCharacter = new Character(name); + CharacterData *newCharacter = new CharacterData(name); // Set the initial attributes provided by the client for (unsigned i = 0; i < mModifiableAttributes.size(); ++i) @@ -829,7 +830,7 @@ void AccountHandler::handleCharacterSelectMessage(AccountClient &client, return; } - Character *selectedChar = chars[slot]; + CharacterData *selectedChar = chars[slot]; std::string address; int port; diff --git a/src/account-server/character.cpp b/src/account-server/character.cpp index 3219e3ce..eafae1c9 100644 --- a/src/account-server/character.cpp +++ b/src/account-server/character.cpp @@ -22,7 +22,7 @@ #include "account-server/account.h" -Character::Character(const std::string &name, int id): +CharacterData::CharacterData(const std::string &name, int id): mName(name), mDatabaseID(id), mCharacterSlot(0), @@ -39,14 +39,14 @@ Character::Character(const std::string &name, int id): { } -void Character::setAccount(Account *acc) +void CharacterData::setAccount(Account *acc) { mAccount = acc; mAccountID = acc->getID(); mAccountLevel = acc->getLevel(); } -void Character::giveSpecial(int id, int currentMana) +void CharacterData::giveSpecial(int id, int currentMana) { if (mSpecials.find(id) == mSpecials.end()) { diff --git a/src/account-server/character.h b/src/account-server/character.h index 0b697392..a9aa810b 100644 --- a/src/account-server/character.h +++ b/src/account-server/character.h @@ -47,6 +47,12 @@ struct AttributeValue double base; /**< Base value of the attribute. */ double modified; /**< Value after various modifiers have been applied. */ + + double getBase() const + { return base; } + + double getModifiedAttribute() const + { return modified; } }; struct SpecialValue @@ -62,6 +68,15 @@ struct SpecialValue unsigned currentMana; }; +struct Status +{ + Status() + : time(0) + {} + + unsigned time; +}; + /** * Stores attributes by their id. */ @@ -72,11 +87,11 @@ typedef std::map<unsigned, AttributeValue> AttributeMap; */ typedef std::map<unsigned, SpecialValue> SpecialMap; -class Character +class CharacterData { public: - Character(const std::string &name, int id = -1); + CharacterData(const std::string &name, int id = -1); /** * Gets the database id of the character. @@ -154,6 +169,9 @@ class Character void setModAttribute(unsigned id, double value) { mAttributes[id].modified = value; } + const AttributeMap &getAttributes() const + { return mAttributes; } + int getSkillSize() const { return mExperience.size(); } @@ -176,15 +194,15 @@ class Character * Get / Set a status effects */ void applyStatusEffect(int id, int time) - { mStatusEffects[id] = time; } + { mStatusEffects[id].time = time; } int getStatusEffectSize() const { return mStatusEffects.size(); } - const std::map<int, int>::const_iterator getStatusEffectBegin() const + const std::map<int, Status>::const_iterator getStatusEffectBegin() const { return mStatusEffects.begin(); } - const std::map<int, int>::const_iterator getStatusEffectEnd() const + const std::map<int, Status>::const_iterator getStatusEffectEnd() const { return mStatusEffects.end(); } /** @@ -266,8 +284,8 @@ class Character private: - Character(const Character &); - Character &operator=(const Character &); + CharacterData(const CharacterData &); + CharacterData &operator=(const CharacterData &); double getAttrBase(AttributeMap::const_iterator &it) const { return it->second.base; } @@ -283,7 +301,7 @@ class Character Point mPos; //!< Position the being is at. AttributeMap mAttributes; //!< Attributes. std::map<int, int> mExperience; //!< Skill Experience. - std::map<int, int> mStatusEffects; //!< Status Effects + std::map<int, Status> mStatusEffects; //!< Status Effects std::map<int, int> mKillCount; //!< Kill Count SpecialMap mSpecials; unsigned short mMapId; //!< Map the being is on. @@ -299,14 +317,11 @@ class Character //!< belongs to. friend class AccountHandler; friend class Storage; - // Set as a friend, but still a lot of redundant accessors. FIXME. - template< class T > - friend void serializeCharacterData(const T &data, MessageOut &msg); }; /** * Type definition for a list of Characters. */ -typedef std::map<unsigned, Character* > Characters; +typedef std::map<unsigned, CharacterData* > Characters; #endif diff --git a/src/account-server/serverhandler.cpp b/src/account-server/serverhandler.cpp index 024266bb..9ed317eb 100644 --- a/src/account-server/serverhandler.cpp +++ b/src/account-server/serverhandler.cpp @@ -156,7 +156,7 @@ bool GameServerHandler::getGameServerFromMap(int mapId, } static void registerGameClient(GameServer *s, const std::string &token, - Character *ptr) + CharacterData *ptr) { MessageOut msg(AGMSG_PLAYER_ENTER); msg.writeString(token, MAGIC_TOKEN_LENGTH); @@ -167,7 +167,7 @@ static void registerGameClient(GameServer *s, const std::string &token, } void GameServerHandler::registerClient(const std::string &token, - Character *ptr) + CharacterData *ptr) { GameServer *s = ::getGameServerFromMap(ptr->getMapId()); assert(s); @@ -292,7 +292,7 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) { LOG_DEBUG("GAMSG_PLAYER_DATA"); int id = msg.readInt32(); - if (Character *ptr = storage->getCharacter(id, NULL)) + if (CharacterData *ptr = storage->getCharacter(id, nullptr)) { deserializeCharacterData(*ptr, msg); if (!storage->updateCharacter(ptr)) @@ -320,7 +320,7 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) LOG_DEBUG("GAMSG_REDIRECT"); int id = msg.readInt32(); std::string magic_token(utils::getMagicToken()); - if (Character *ptr = storage->getCharacter(id, NULL)) + if (CharacterData *ptr = storage->getCharacter(id, nullptr)) { int mapId = ptr->getMapId(); if (GameServer *s = getGameServerFromMap(mapId)) @@ -353,7 +353,7 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) int id = msg.readInt32(); std::string magic_token = msg.readString(MAGIC_TOKEN_LENGTH); - if (Character *ptr = storage->getCharacter(id, NULL)) + if (CharacterData *ptr = storage->getCharacter(id, nullptr)) { int accountID = ptr->getAccountID(); AccountClientHandler::prepareReconnect(magic_token, accountID); @@ -432,7 +432,7 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) int level = msg.readInt16(); // get the character so we can get the account id - Character *c = storage->getCharacter(id, NULL); + CharacterData *c = storage->getCharacter(id, NULL); if (c) { storage->setAccountLevel(c->getAccountID(), level); @@ -478,7 +478,7 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) result.writeInt32(characterId); // get the character based on the id - Character *ptr = storage->getCharacter(characterId, NULL); + CharacterData *ptr = storage->getCharacter(characterId, nullptr); if (!ptr) { // Invalid character @@ -528,8 +528,8 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) result.writeInt32(senderId); // get their characters - Character *sender = storage->getCharacter(senderId, NULL); - Character *receiver = storage->getCharacter(receiverName); + CharacterData *sender = storage->getCharacter(senderId, NULL); + CharacterData *receiver = storage->getCharacter(receiverName); if (!sender || !receiver) { // Invalid character @@ -657,7 +657,7 @@ void GameServerHandler::dumpStatistics(std::ostream &os) } } -void GameServerHandler::sendPartyChange(Character *ptr, int partyId) +void GameServerHandler::sendPartyChange(CharacterData *ptr, int partyId) { GameServer *s = ::getGameServerFromMap(ptr->getMapId()); if (s) diff --git a/src/account-server/serverhandler.h b/src/account-server/serverhandler.h index f4499f1b..6f4c61fe 100644 --- a/src/account-server/serverhandler.h +++ b/src/account-server/serverhandler.h @@ -26,7 +26,7 @@ #include "net/messagein.h" -class Character; +class CharacterData; namespace GameServerHandler { @@ -49,7 +49,7 @@ namespace GameServerHandler /** * Warns a game server about a soon-to-connect client. */ - void registerClient(const std::string &token, Character *); + void registerClient(const std::string &token, CharacterData *); /** * Dumps per-server statistics into given stream @@ -64,7 +64,7 @@ namespace GameServerHandler /** * Sends chat party information */ - void sendPartyChange(Character *ptr, int partyId); + void sendPartyChange(CharacterData *ptr, int partyId); /** * Takes a GAMSG_PLAYER_SYNC from the gameserver and stores all changes in diff --git a/src/account-server/storage.cpp b/src/account-server/storage.cpp index 067967e1..dd8cf9ac 100644 --- a/src/account-server/storage.cpp +++ b/src/account-server/storage.cpp @@ -223,7 +223,8 @@ Account *Storage::getAccountBySQL() for (int k = 0; k < size; ++k) { - if (Character *ptr = getCharacter(characterIDs[k], account)) + if (CharacterData *ptr = + getCharacter(characterIDs[k], account)) { characters[ptr->getCharacterSlot()] = ptr; } @@ -339,9 +340,9 @@ Account *Storage::getAccount(int accountID) return 0; } -Character *Storage::getCharacterBySQL(Account *owner) +CharacterData *Storage::getCharacterBySQL(Account *owner) { - Character *character = 0; + CharacterData *character = 0; string_to< unsigned > toUint; string_to< int > toInt; @@ -358,7 +359,7 @@ Character *Storage::getCharacterBySQL(Account *owner) string_to< unsigned short > toUshort; string_to< double > toDouble; - character = new Character(charInfo(0, 2), toUint(charInfo(0, 0))); + character = new CharacterData(charInfo(0, 2), toUint(charInfo(0, 0))); character->setGender(toUshort(charInfo(0, 3))); character->setHairStyle(toUshort(charInfo(0, 4))); character->setHairColor(toUshort(charInfo(0, 5))); @@ -559,7 +560,7 @@ Character *Storage::getCharacterBySQL(Account *owner) return character; } -Character *Storage::getCharacter(int id, Account *owner) +CharacterData *Storage::getCharacter(int id, Account *owner) { std::ostringstream sql; sql << "SELECT * FROM " << CHARACTERS_TBL_NAME << " WHERE id = ?"; @@ -571,7 +572,7 @@ Character *Storage::getCharacter(int id, Account *owner) return 0; } -Character *Storage::getCharacter(const std::string &name) +CharacterData *Storage::getCharacter(const std::string &name) { std::ostringstream sql; sql << "SELECT * FROM " << CHARACTERS_TBL_NAME << " WHERE name = ?"; @@ -708,7 +709,7 @@ bool Storage::doesCharacterNameExist(const std::string& name) return true; } -bool Storage::updateCharacter(Character *character) +bool Storage::updateCharacter(CharacterData *character) { dal::PerformTransaction transaction(mDb); @@ -909,12 +910,12 @@ bool Storage::updateCharacter(Character *character) } try { - std::map<int, int>::const_iterator status_it; + std::map<int, Status>::const_iterator status_it; for (status_it = character->getStatusEffectBegin(); status_it != character->getStatusEffectEnd(); status_it++) { insertStatusEffect(character->getDatabaseID(), - status_it->first, status_it->second); + status_it->first, status_it->second.time); } } catch (const dal::DbSqlQueryExecFailure& e) @@ -1007,7 +1008,7 @@ void Storage::flush(Account *account) for (Characters::const_iterator it = characters.begin(), it_end = characters.end(); it != it_end; ++it) { - Character *character = (*it).second; + CharacterData *character = (*it).second; if (character->getDatabaseID() >= 0) { updateCharacter(character); @@ -1541,7 +1542,7 @@ std::map<int, Guild*> Storage::getGuildList() std::list<std::pair<int, int> >::const_iterator i, i_end; for (i = members.begin(), i_end = members.end(); i != i_end; ++i) { - Character *character = getCharacter((*i).first, 0); + CharacterData *character = getCharacter((*i).first, 0); if (character) { character->addGuild(it->second->getName()); @@ -1844,7 +1845,7 @@ void Storage::delCharacter(int charId) const } } -void Storage::delCharacter(Character *character) const +void Storage::delCharacter(CharacterData *character) const { delCharacter(character->getDatabaseID()); } @@ -1999,8 +2000,8 @@ Post *Storage::getStoredPost(int playerId) for (unsigned i = 0; i < post.rows(); i++ ) { // Load sender and receiver - Character *sender = getCharacter(toUint(post(i, 1)), 0); - Character *receiver = getCharacter(toUint(post(i, 2)), 0); + CharacterData *sender = getCharacter(toUint(post(i, 1)), 0); + CharacterData *receiver = getCharacter(toUint(post(i, 2)), 0); Letter *letter = new Letter(toUint( post(0,3) ), sender, receiver); diff --git a/src/account-server/storage.h b/src/account-server/storage.h index 06645c69..fbc4cd8c 100644 --- a/src/account-server/storage.h +++ b/src/account-server/storage.h @@ -30,7 +30,7 @@ #include "common/transaction.h" class Account; -class Character; +class CharacterData; class ChatChannel; class FloorItem; class Guild; @@ -83,7 +83,7 @@ class Storage * * @return the character associated to the Id. */ - Character *getCharacter(int id, Account *owner); + CharacterData *getCharacter(int id, Account *owner); /** * Gets a character by character name. @@ -92,7 +92,7 @@ class Storage * * @return the character associated to the name */ - Character *getCharacter(const std::string &name); + CharacterData *getCharacter(const std::string &name); /** * Gets the id of a character by its name. @@ -194,7 +194,7 @@ class Storage * * @param character character object. */ - void delCharacter(Character *character) const; + void delCharacter(CharacterData *character) const; /** * Removes expired bans from accounts @@ -238,7 +238,7 @@ class Storage * * @return true on success */ - bool updateCharacter(Character *ptr); + bool updateCharacter(CharacterData *ptr); /** * Add a new guild. @@ -475,7 +475,7 @@ class Storage * * @return the character found by the query. */ - Character *getCharacterBySQL(Account *owner); + CharacterData *getCharacterBySQL(Account *owner); /** * Fix improper character slots diff --git a/src/chat-server/chathandler.cpp b/src/chat-server/chathandler.cpp index 63aaa43a..209c31cb 100644 --- a/src/chat-server/chathandler.cpp +++ b/src/chat-server/chathandler.cpp @@ -83,7 +83,7 @@ void ChatHandler::tokenMatched(ChatClient *client, Pending *p) client->characterName = p->character; client->accountLevel = p->level; - Character *c = storage->getCharacter(p->character); + CharacterData *c = storage->getCharacter(p->character); if (!c) { @@ -443,7 +443,7 @@ void ChatHandler::handleModeChangeMessage(ChatClient &client, MessageIn &msg) trans.mCharacterId = client.characterId; trans.mAction = TRANS_CHANNEL_MODE; trans.mMessage = "User mode "; - trans.mMessage.append(mode + " set on " + user); + trans.mMessage.append(utils::toString(mode) + " set on " + user); storage->addTransaction(trans); } diff --git a/src/chat-server/guildhandler.cpp b/src/chat-server/guildhandler.cpp index 1bbe3671..da666769 100644 --- a/src/chat-server/guildhandler.cpp +++ b/src/chat-server/guildhandler.cpp @@ -127,7 +127,7 @@ void ChatHandler::sendGuildListUpdate(Guild *guild, for (std::list<GuildMember*>::const_iterator itr = members.begin(); itr != members.end(); ++itr) { - Character *c = storage->getCharacter((*itr)->mId, NULL); + CharacterData *c = storage->getCharacter((*itr)->mId, nullptr); chr = mPlayerMap.find(c->getName()); if (chr != mPlayerMap.end()) { @@ -293,7 +293,7 @@ void ChatHandler::handleGuildGetMembers(ChatClient &client, MessageIn &msg) for (std::list<GuildMember*>::iterator itr = memberList.begin(); itr != itr_end; ++itr) { - Character *c = storage->getCharacter((*itr)->mId, NULL); + CharacterData *c = storage->getCharacter((*itr)->mId, nullptr); std::string memberName = c->getName(); reply.writeString(memberName); reply.writeInt8(mPlayerMap.find(memberName) != mPlayerMap.end()); @@ -318,7 +318,7 @@ void ChatHandler::handleGuildMemberLevelChange(ChatClient &client, std::string user = msg.readString(); short level = msg.readInt8(); Guild *guild = guildManager->findById(guildId); - Character *c = storage->getCharacter(user); + CharacterData *c = storage->getCharacter(user); if (guild && c) { diff --git a/src/chat-server/partyhandler.cpp b/src/chat-server/partyhandler.cpp index 16d43e68..1e5ad6b9 100644 --- a/src/chat-server/partyhandler.cpp +++ b/src/chat-server/partyhandler.cpp @@ -34,7 +34,7 @@ using namespace ManaServ; void updateInfo(ChatClient *client, int partyId) { - Character *character = storage->getCharacter(client->characterName); + CharacterData *character = storage->getCharacter(client->characterName); GameServerHandler::sendPartyChange(character, partyId); } diff --git a/src/chat-server/post.cpp b/src/chat-server/post.cpp index dc1e0d1e..6db5f768 100644 --- a/src/chat-server/post.cpp +++ b/src/chat-server/post.cpp @@ -23,7 +23,7 @@ #include "../account-server/character.h" #include "../common/configuration.h" -Letter::Letter(unsigned type, Character *sender, Character *receiver) +Letter::Letter(unsigned type, CharacterData *sender, CharacterData *receiver) : mId(0), mType(type), mSender(sender), mReceiver(receiver) { } @@ -70,12 +70,12 @@ bool Letter::addAttachment(InventoryItem item) return true; } -Character *Letter::getReceiver() const +CharacterData *Letter::getReceiver() const { return mReceiver; } -Character *Letter::getSender() const +CharacterData *Letter::getSender() const { return mSender; } @@ -127,7 +127,7 @@ unsigned Post::getNumberOfLetters() const void PostManager::addLetter(Letter *letter) { - std::map<Character*, Post*>::iterator itr = + std::map<CharacterData*, Post*>::iterator itr = mPostBox.find(letter->getReceiver()); if (itr != mPostBox.end()) { @@ -138,20 +138,20 @@ void PostManager::addLetter(Letter *letter) Post *post = new Post(); post->addLetter(letter); mPostBox.insert( - std::pair<Character*, Post*>(letter->getReceiver(), post) + std::pair<CharacterData*, Post*>(letter->getReceiver(), post) ); } } -Post *PostManager::getPost(Character *player) const +Post *PostManager::getPost(CharacterData *player) const { - std::map<Character*, Post*>::const_iterator itr = mPostBox.find(player); + std::map<CharacterData*, Post*>::const_iterator itr = mPostBox.find(player); return (itr == mPostBox.end()) ? NULL : itr->second; } -void PostManager::clearPost(Character *player) +void PostManager::clearPost(CharacterData *player) { - std::map<Character*, Post*>::iterator itr = + std::map<CharacterData*, Post*>::iterator itr = mPostBox.find(player); if (itr != mPostBox.end()) { diff --git a/src/chat-server/post.h b/src/chat-server/post.h index 2d0b2ae0..88a85489 100644 --- a/src/chat-server/post.h +++ b/src/chat-server/post.h @@ -27,8 +27,7 @@ #include "../common/inventorydata.h" -class Item; -class Character; +class CharacterData; class Letter { @@ -42,7 +41,7 @@ public: * @param sender Pointer to character that sent the letter * @param receiver Pointer to character that will receive the letter */ - Letter(unsigned type, Character *sender, Character *receiver); + Letter(unsigned type, CharacterData *sender, CharacterData *receiver); ~Letter(); @@ -99,13 +98,13 @@ public: * Get the character receiving the letter * @return Returns the Character who will receive the letter */ - Character *getReceiver() const; + CharacterData *getReceiver() const; /** * Get the character who sent the letter * @return Returns the Character who sent the letter */ - Character *getSender() const; + CharacterData *getSender() const; /** * Get the attachments @@ -118,8 +117,8 @@ private: unsigned long mExpiry; std::string mContents; std::vector<InventoryItem> mAttachments; - Character *mSender; - Character *mReceiver; + CharacterData *mSender; + CharacterData *mReceiver; }; class Post @@ -163,15 +162,15 @@ public: * @param player Character that is getting post * @return Returns the post for that character */ - Post *getPost(Character *player) const; + Post *getPost(CharacterData *player) const; /** * Remove the post for character */ - void clearPost(Character *player); + void clearPost(CharacterData *player); private: - std::map<Character*, Post*> mPostBox; + std::map<CharacterData*, Post*> mPostBox; }; extern PostManager *postalManager; diff --git a/src/common/permissionmanager.cpp b/src/common/permissionmanager.cpp index 573e1d26..c5c87f66 100644 --- a/src/common/permissionmanager.cpp +++ b/src/common/permissionmanager.cpp @@ -37,7 +37,7 @@ void addPermission(std::string permission, char mask) std::map<std::string, unsigned char>::iterator i = permissions.find(permission); if (i == permissions.end()) { - permissions.insert(std::make_pair<std::string, unsigned char>(permission, mask)); + permissions.insert(std::make_pair(permission, mask)); } else { i->second |= mask; } @@ -105,9 +105,10 @@ void PermissionManager::reload() } -PermissionManager::Result PermissionManager::checkPermission(const Character* character, std::string permission) +PermissionManager::Result PermissionManager::checkPermission(const Entity* character, std::string permission) { - return checkPermission(character->getAccountLevel(), permission); + return checkPermission(character->getComponent<CharacterComponent>() + ->getAccountLevel(), permission); } PermissionManager::Result PermissionManager::checkPermission(unsigned char level, std::string permission) @@ -139,12 +140,13 @@ unsigned char PermissionManager::getMaskFromAlias(const std::string &alias) } } -std::list<std::string> PermissionManager::getPermissionList(const Character* character) +std::list<std::string> PermissionManager::getPermissionList(const Entity* character) { std::list<std::string> result; std::map<std::string, unsigned char>::iterator i; - unsigned char mask = character->getAccountLevel(); + unsigned char mask = character->getComponent<CharacterComponent>() + ->getAccountLevel(); for (i = permissions.begin(); i != permissions.end(); i++) { @@ -157,12 +159,13 @@ std::list<std::string> PermissionManager::getPermissionList(const Character* cha return result; } -std::list<std::string> PermissionManager::getClassList(const Character* character) +std::list<std::string> PermissionManager::getClassList(const Entity* character) { std::list<std::string> result; std::map<std::string, unsigned char>::iterator i; - unsigned char mask = character->getAccountLevel(); + unsigned char mask = character->getComponent<CharacterComponent>() + ->getAccountLevel(); for (i = aliases.begin(); i != aliases.end(); i++) { diff --git a/src/common/permissionmanager.h b/src/common/permissionmanager.h index bf36f658..2189f89f 100644 --- a/src/common/permissionmanager.h +++ b/src/common/permissionmanager.h @@ -25,7 +25,7 @@ #include <map> #include <string> -class Character; +class Entity; namespace PermissionManager { @@ -48,7 +48,7 @@ namespace PermissionManager /** * Returns if the characters account has the given permission */ - Result checkPermission(const Character* character, std::string permission); + Result checkPermission(const Entity *character, std::string permission); Result checkPermission(unsigned char level, std::string permission); /** @@ -59,12 +59,12 @@ namespace PermissionManager /** * Gets a list of all permissions the character is having */ - std::list<std::string> getPermissionList(const Character* character); + std::list<std::string> getPermissionList(const Entity* character); /** * Gets a list of all permissions classes the character is having */ - std::list<std::string> getClassList(const Character* character); + std::list<std::string> getClassList(const Entity* character); } // namespace PermissionManager diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp index 8b517f54..e52393bd 100644 --- a/src/game-server/accountconnection.cpp +++ b/src/game-server/accountconnection.cpp @@ -102,11 +102,12 @@ bool AccountConnection::start(int gameServerPort) return true; } -void AccountConnection::sendCharacterData(Character *p) +void AccountConnection::sendCharacterData(Entity *p) { MessageOut msg(GAMSG_PLAYER_DATA); - msg.writeInt32(p->getDatabaseID()); - serializeCharacterData(*p, msg); + auto *characterComponent = p->getComponent<CharacterComponent>(); + msg.writeInt32(characterComponent->getDatabaseID()); + serializeCharacterData(CharacterData(p, characterComponent), msg); send(msg); } @@ -154,8 +155,11 @@ void AccountConnection::processMessage(MessageIn &msg) case AGMSG_PLAYER_ENTER: { std::string token = msg.readString(MAGIC_TOKEN_LENGTH); - Character *ptr = new Character(msg); - gameHandler->addPendingCharacter(token, ptr); + Entity *character = new Entity(OBJECT_CHARACTER); + character->addComponent(new ActorComponent(*character)); + character->addComponent(new BeingComponent(*character)); + character->addComponent(new CharacterComponent(*character, msg)); + gameHandler->addPendingCharacter(token, character); } break; case AGMSG_ACTIVE_MAP: @@ -187,16 +191,15 @@ void AccountConnection::processMessage(MessageIn &msg) if (ItemClass *ic = itemManager->getItem(itemId)) { - Item *item = new Item(ic, amount); - item->setMap(m); - Point dst(posX, posY); - item->setPosition(dst); + Entity *item = Item::create(m, + Point(posX, posY), + ic, amount); if (!GameState::insertOrDelete(item)) { // The map is full. LOG_WARN("Couldn't add floor item(s) " << itemId - << " into map " << mapId); + << " into map " << mapId); return; } } @@ -242,7 +245,7 @@ void AccountConnection::processMessage(MessageIn &msg) case CGMSG_POST_RESPONSE: { // get the character - Character *character = postMan->getCharacter(msg.readInt32()); + Entity *character = postMan->getCharacter(msg.readInt32()); // check character is still valid if (!character) @@ -260,7 +263,7 @@ void AccountConnection::processMessage(MessageIn &msg) case CGMSG_STORE_POST_RESPONSE: { // get character - Character *character = postMan->getCharacter(msg.readInt32()); + Entity *character = postMan->getCharacter(msg.readInt32()); // check character is valid if (!character) @@ -289,21 +292,21 @@ void AccountConnection::playerReconnectAccount(int id, send(msg); } -void AccountConnection::requestCharacterVar(Character *ch, +void AccountConnection::requestCharacterVar(Entity *ch, const std::string &name) { MessageOut msg(GAMSG_GET_VAR_CHR); - msg.writeInt32(ch->getDatabaseID()); + msg.writeInt32(ch->getComponent<CharacterComponent>()->getDatabaseID()); msg.writeString(name); send(msg); } -void AccountConnection::updateCharacterVar(Character *ch, +void AccountConnection::updateCharacterVar(Entity *ch, const std::string &name, const std::string &value) { MessageOut msg(GAMSG_SET_VAR_CHR); - msg.writeInt32(ch->getDatabaseID()); + msg.writeInt32(ch->getComponent<CharacterComponent>()->getDatabaseID()); msg.writeString(name); msg.writeString(value); send(msg); @@ -329,10 +332,10 @@ void AccountConnection::updateWorldVar(const std::string &name, send(msg); } -void AccountConnection::banCharacter(Character *ch, int duration) +void AccountConnection::banCharacter(Entity *ch, int duration) { MessageOut msg(GAMSG_BAN_PLAYER); - msg.writeInt32(ch->getDatabaseID()); + msg.writeInt32(ch->getComponent<CharacterComponent>()->getDatabaseID()); msg.writeInt32(duration); send(msg); } @@ -358,9 +361,12 @@ void AccountConnection::sendStatistics() switch (t->getType()) { case OBJECT_CHARACTER: - players.push_back - (static_cast< Character * >(t)->getDatabaseID()); + { + auto *characterComponent = + t->getComponent<CharacterComponent>(); + players.push_back(characterComponent->getDatabaseID()); break; + } case OBJECT_MONSTER: ++nbMonsters; break; @@ -380,13 +386,13 @@ void AccountConnection::sendStatistics() send(msg); } -void AccountConnection::sendPost(Character *c, MessageIn &msg) +void AccountConnection::sendPost(Entity *c, MessageIn &msg) { // send message to account server with id of sending player, // the id of receiving player, the letter receiver and contents, and attachments LOG_DEBUG("Sending GCMSG_STORE_POST."); MessageOut out(GCMSG_STORE_POST); - out.writeInt32(c->getDatabaseID()); + out.writeInt32(c->getComponent<CharacterComponent>()->getDatabaseID()); out.writeString(msg.readString()); // name of receiver out.writeString(msg.readString()); // content of letter while (msg.getUnreadLength()) // attachments @@ -398,7 +404,7 @@ void AccountConnection::sendPost(Character *c, MessageIn &msg) send(out); } -void AccountConnection::getPost(Character *c) +void AccountConnection::getPost(Entity *c) { // let the postman know to expect some post for this character postMan->addCharacter(c); @@ -406,14 +412,14 @@ void AccountConnection::getPost(Character *c) // send message to account server with id of retrieving player LOG_DEBUG("Sending GCMSG_REQUEST_POST"); MessageOut out(GCMSG_REQUEST_POST); - out.writeInt32(c->getDatabaseID()); + out.writeInt32(c->getComponent<CharacterComponent>()->getDatabaseID()); send(out); } -void AccountConnection::changeAccountLevel(Character *c, int level) +void AccountConnection::changeAccountLevel(Entity *c, int level) { MessageOut msg(GAMSG_CHANGE_ACCOUNT_LEVEL); - msg.writeInt32(c->getDatabaseID()); + msg.writeInt32(c->getComponent<CharacterComponent>()->getDatabaseID()); msg.writeInt16(level); send(msg); } diff --git a/src/game-server/accountconnection.h b/src/game-server/accountconnection.h index a144a1d1..2ad79dd2 100644 --- a/src/game-server/accountconnection.h +++ b/src/game-server/accountconnection.h @@ -24,7 +24,7 @@ #include "net/messageout.h" #include "net/connection.h" -class Character; +class Entity; class MapComposite; /** @@ -45,7 +45,7 @@ class AccountConnection : public Connection /** * Sends data of a given character. */ - void sendCharacterData(Character *); + void sendCharacterData(Entity *); /** * Prepares the account server for a reconnecting player @@ -55,19 +55,19 @@ class AccountConnection : public Connection /** * Requests the value of a character-bound variable from the database. */ - void requestCharacterVar(Character *, const std::string &); + void requestCharacterVar(Entity *, const std::string &); /** * Pushes a new character-bound value to the database. */ - void updateCharacterVar(Character *, const std::string &name, - const std::string &value); + void updateCharacterVar(Entity *, const std::string &name, + const std::string &value); /** * Pushes a new value of a map variable to the account server. */ void updateMapVar(MapComposite *, const std::string &name, - const std::string &value); + const std::string &value); /** * Pushes a new value of a world variable to the account server. @@ -78,7 +78,7 @@ class AccountConnection : public Connection /** * Sends ban message. */ - void banCharacter(Character *, int); + void banCharacter(Entity *, int); /** * Gathers statistics and sends them. @@ -88,17 +88,17 @@ class AccountConnection : public Connection /** * Send letter */ - void sendPost(Character *, MessageIn &); + void sendPost(Entity *, MessageIn &); /** * Get post */ - void getPost(Character *); + void getPost(Entity *); /** * Change Account Level */ - void changeAccountLevel(Character *, int); + void changeAccountLevel(Entity *, int); /** * Sends all changed player data to the account server to minimize diff --git a/src/game-server/actor.cpp b/src/game-server/actor.cpp index 60379124..9c839172 100644 --- a/src/game-server/actor.cpp +++ b/src/game-server/actor.cpp @@ -25,10 +25,24 @@ #include <cassert> -Actor::~Actor() +ActorComponent::ActorComponent(Entity &entity): + mMoveTime(0), + mUpdateFlags(0), + mPublicID(65535), + mSize(0), + mWalkMask(0), + mBlockType(BLOCKTYPE_NONE) +{ + entity.signal_removed.connect( + sigc::mem_fun(this, &ActorComponent::removed)); + entity.signal_map_changed.connect( + sigc::mem_fun(this, &ActorComponent::mapChanged)); +} + +void ActorComponent::removed(Entity *entity) { // Free the map position - if (MapComposite *mapComposite = getMap()) + if (MapComposite *mapComposite = entity->getMap()) { Map *map = mapComposite->getMap(); int tileWidth = map->getTileWidth(); @@ -38,10 +52,10 @@ Actor::~Actor() } } -void Actor::setPosition(const Point &p) +void ActorComponent::setPosition(Entity &entity, const Point &p) { // Update blockmap - if (MapComposite *mapComposite = getMap()) + if (MapComposite *mapComposite = entity.getMap()) { Map *map = mapComposite->getMap(); int tileWidth = map->getTileWidth(); @@ -58,21 +72,11 @@ void Actor::setPosition(const Point &p) mPos = p; } -void Actor::setMap(MapComposite *mapComposite) +void ActorComponent::mapChanged(Entity *entity) { - assert(mapComposite); const Point p = getPosition(); - if (MapComposite *oldMapComposite = getMap()) - { - Map *oldMap = oldMapComposite->getMap(); - int oldTileWidth = oldMap->getTileWidth(); - int oldTileHeight = oldMap->getTileHeight(); - oldMap->freeTile(p.x / oldTileWidth, p.y / oldTileHeight, - getBlockType()); - } - Entity::setMap(mapComposite); - Map *map = mapComposite->getMap(); + Map *map = entity->getMap()->getMap(); int tileWidth = map->getTileWidth(); int tileHeight = map->getTileHeight(); map->blockTile(p.x / tileWidth, p.y / tileHeight, getBlockType()); diff --git a/src/game-server/actor.h b/src/game-server/actor.h index 13d4af55..2950d512 100644 --- a/src/game-server/actor.h +++ b/src/game-server/actor.h @@ -45,19 +45,17 @@ enum * Generic client-visible object. Keeps track of position, size and what to * update clients about. */ -class Actor : public Entity +class ActorComponent : public Component { public: - Actor(EntityType type) - : Entity(type), - mMoveTime(0), - mUpdateFlags(0), - mPublicID(65535), - mSize(0), - mWalkMask(0) + static const ComponentType type = CT_Actor; + + ActorComponent(Entity &entity); + + void update(Entity &entity) {} - ~Actor(); + void removed(Entity *entity); /** * Sets the coordinates. Also updates the walkmap of the map the actor @@ -65,7 +63,7 @@ class Actor : public Entity * * @param p the coordinates. */ - void setPosition(const Point &p); + void setPosition(Entity &entity, const Point &p); /** * Gets the coordinates. @@ -126,16 +124,20 @@ class Actor : public Entity { return mWalkMask; } /** - * Overridden in order to update the walkmap. + * Gets the way the actor blocks pathfinding for other actors. */ - virtual void setMap(MapComposite *map); + BlockType getBlockType() const + { return mBlockType; } + + void setBlockType(BlockType blockType) + { mBlockType = blockType; } - protected: /** - * Gets the way the actor blocks pathfinding for other actors. + * Overridden in order to update the walkmap. */ - virtual BlockType getBlockType() const - { return BLOCKTYPE_NONE; } + virtual void mapChanged(Entity *entity); + + protected: /** Delay until move to next tile in miliseconds. */ unsigned short mMoveTime; @@ -150,6 +152,7 @@ class Actor : public Entity unsigned char mSize; /**< Radius of bounding circle. */ unsigned char mWalkMask; + BlockType mBlockType; }; #endif // ACTOR_H diff --git a/src/game-server/attack.cpp b/src/game-server/attack.cpp index 1825b86a..6ca85fb8 100644 --- a/src/game-server/attack.cpp +++ b/src/game-server/attack.cpp @@ -72,12 +72,13 @@ AttackInfo *AttackInfo::readAttackNode(xmlNodePtr node) return attack; } -void Attacks::add(AttackInfo *attackInfo) +void Attacks::add(CombatComponent *combatComponent, AttackInfo *attackInfo) { mAttacks.push_back(Attack(attackInfo)); + attack_added.emit(combatComponent, *mAttacks.rbegin()); } -void Attacks::remove(AttackInfo *attackInfo) +void Attacks::remove(CombatComponent *combatComponent, AttackInfo *attackInfo) { for (std::vector<Attack>::iterator it = mAttacks.begin(), it_end = mAttacks.end(); it != it_end; ++it) @@ -86,6 +87,7 @@ void Attacks::remove(AttackInfo *attackInfo) { if (mCurrentAttack && mCurrentAttack->getAttackInfo() == attackInfo) mCurrentAttack = 0; + attack_removed.emit(combatComponent, *it); mAttacks.erase(it); return; } diff --git a/src/game-server/attack.h b/src/game-server/attack.h index 0bcc361c..8dc62629 100644 --- a/src/game-server/attack.h +++ b/src/game-server/attack.h @@ -21,9 +21,13 @@ #ifndef ATTACK_H #define ATTACK_H +#include <cassert> #include <cstddef> #include <list> +#include <sigc++/signal.h> +#include <sigc++/trackable.h> + #include "common/defines.h" #include "scripting/script.h" @@ -32,6 +36,8 @@ #include "game-server/timeout.h" +class CombatComponent; + /** * Structure that describes the severity and nature of an attack a being can * be hit by. @@ -65,7 +71,7 @@ struct Damage * Class that stores information about an auto-attack */ -class Character; +class CharacterComponent; struct AttackInfo { @@ -141,7 +147,7 @@ class Attack public: Attack(AttackInfo *info): mInfo(info) - {} + {assert(info);} AttackInfo *getAttackInfo() { return mInfo; } @@ -168,15 +174,15 @@ class Attack /** * Helper class for storing multiple auto-attacks. */ -class Attacks +class Attacks : public sigc::trackable { public: Attacks(): mCurrentAttack(0) {} - void add(AttackInfo *); - void remove(AttackInfo *); + void add(CombatComponent *combatComponent, AttackInfo *); + void remove(CombatComponent *combatComponent, AttackInfo *); void markAttackAsTriggered(); Attack *getTriggerableAttack(); void startAttack(Attack *attack); @@ -188,6 +194,9 @@ class Attacks unsigned getNumber() { return mAttacks.size(); } + sigc::signal<void, CombatComponent *, Attack &> attack_added; + sigc::signal<void, CombatComponent *, Attack &> attack_removed; + private: std::vector<Attack> mAttacks; diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp index 66545ff6..6f5b8586 100644 --- a/src/game-server/being.cpp +++ b/src/game-server/being.cpp @@ -27,6 +27,7 @@ #include "game-server/attributemanager.h" #include "game-server/character.h" #include "game-server/collisiondetection.h" +#include "game-server/combatcomponent.h" #include "game-server/mapcomposite.h" #include "game-server/effect.h" #include "game-server/skillmanager.h" @@ -37,15 +38,13 @@ #include "scripting/scriptmanager.h" -Script::Ref Being::mRecalculateDerivedAttributesCallback; -Script::Ref Being::mRecalculateBaseAttributeCallback; +Script::Ref BeingComponent::mRecalculateDerivedAttributesCallback; +Script::Ref BeingComponent::mRecalculateBaseAttributeCallback; -Being::Being(EntityType type): - Actor(type), +BeingComponent::BeingComponent(Entity &entity): + mMoveTime(0), mAction(STAND), - mTarget(NULL), mGender(GENDER_UNSPECIFIED), - mCurrentAttack(0), mDirection(DOWN), mEmoteId(0) { @@ -61,10 +60,12 @@ Being::Being(EntityType type): LOG_DEBUG("Attempting to create attribute '" << it1->first << "'."); mAttributes.insert(std::make_pair(it1->first, Attribute(*it1->second))); - } - signal_inserted.connect(sigc::mem_fun(this, &Being::inserted)); + clearDestination(entity); + + entity.signal_inserted.connect(sigc::mem_fun(this, + &BeingComponent::inserted)); // TODO: Way to define default base values? // Should this be handled by the virtual modifiedAttribute? @@ -79,84 +80,17 @@ Being::Being(EntityType type): #endif } -void Being::triggerEmote(int id) +void BeingComponent::triggerEmote(Entity &entity, int id) { mEmoteId = id; if (id > -1) - raiseUpdateFlags(UPDATEFLAG_EMOTE); + entity.getComponent<ActorComponent>()->raiseUpdateFlags(UPDATEFLAG_EMOTE); } -int Being::damage(Actor * /* source */, const Damage &damage) -{ - if (mAction == DEAD) - return 0; - - int HPloss = damage.base; - if (damage.delta) - HPloss += rand() * (damage.delta + 1) / RAND_MAX; - - // TODO magical attacks and associated elemental modifiers - switch (damage.type) - { - case DAMAGE_PHYSICAL: - if (!damage.trueStrike && - rand()%((int) getModifiedAttribute(ATTR_DODGE) + 1) > - rand()%(damage.cth + 1)) - { - HPloss = 0; - // TODO Process triggers for a dodged physical attack here. - // If there is an attacker included, also process triggers for the attacker (failed physical strike) - } - else - { - HPloss = HPloss * (1.0 - (0.0159375f * - getModifiedAttribute(ATTR_DEFENSE)) / - (1.0 + 0.017 * - getModifiedAttribute(ATTR_DEFENSE))) + - (rand()%((HPloss >> 4) + 1)); - // TODO Process triggers for receiving damage here. - // If there is an attacker included, also process triggers for the attacker (successful physical strike) - } - break; - case DAMAGE_MAGICAL: -#if 0 - getModifiedAttribute(BASE_ELEM_BEGIN + damage.element); -#else - LOG_WARN("Attempt to use magical type damage! This has not been" - "implemented yet and should not be used!"); - HPloss = 0; -#endif - break; - case DAMAGE_DIRECT: - break; - default: - LOG_WARN("Unknown damage type '" << damage.type << "'!"); - break; - } - - if (HPloss > 0) - { - mHitsTaken.push_back(HPloss); - Attribute &HP = mAttributes.at(ATTR_HP); - LOG_DEBUG("Being " << getPublicID() << " suffered " << HPloss - << " damage. HP: " - << HP.getModifiedAttribute() << "/" - << mAttributes.at(ATTR_MAX_HP).getModifiedAttribute()); - setAttribute(ATTR_HP, HP.getBase() - HPloss); - // No HP regen after being hit if this is set. - mHealthRegenerationTimeout.setSoft( - Configuration::getValue("game_hpRegenBreakAfterHit", 0)); - } - else - { - HPloss = 0; - } - return HPloss; -} -void Being::heal() +void BeingComponent::heal(Entity &entity) { Attribute &hp = mAttributes.at(ATTR_HP); Attribute &maxHp = mAttributes.at(ATTR_MAX_HP); @@ -165,10 +99,10 @@ void Being::heal() // Reset all modifications present in hp. hp.clearMods(); - setAttribute(ATTR_HP, maxHp.getModifiedAttribute()); + setAttribute(entity, ATTR_HP, maxHp.getModifiedAttribute()); } -void Being::heal(int gain) +void BeingComponent::heal(Entity &entity, int gain) { Attribute &hp = mAttributes.at(ATTR_HP); Attribute &maxHp = mAttributes.at(ATTR_MAX_HP); @@ -176,106 +110,64 @@ void Being::heal(int gain) return; // Full hp, do nothing. // Cannot go over maximum hitpoints. - setAttribute(ATTR_HP, hp.getBase() + gain); + setAttribute(entity, ATTR_HP, hp.getBase() + gain); if (hp.getModifiedAttribute() > maxHp.getModifiedAttribute()) - setAttribute(ATTR_HP, maxHp.getModifiedAttribute()); + setAttribute(entity, ATTR_HP, maxHp.getModifiedAttribute()); } -void Being::died() +void BeingComponent::died(Entity &entity) { if (mAction == DEAD) return; - LOG_DEBUG("Being " << getPublicID() << " died."); - setAction(DEAD); + LOG_DEBUG("Being " << entity.getComponent<ActorComponent>()->getPublicID() + << " died."); + setAction(entity, DEAD); // dead beings stay where they are - clearDestination(); - - // reset target - mTarget = NULL; + clearDestination(entity); - signal_died.emit(this); + signal_died.emit(&entity); } -void Being::processAttacks() +void BeingComponent::setDestination(Entity &entity, const Point &dst) { - if (mAction != ATTACK || !mTarget) - return; - - // Ticks attacks even when not attacking to permit cooldowns and warmups. - std::vector<Attack *> attacksReady; - mAttacks.getUsuableAttacks(&attacksReady); - - if (Attack *triggerableAttack = mAttacks.getTriggerableAttack()) - { - processAttack(*triggerableAttack); - mAttacks.markAttackAsTriggered(); - } - - // Deal with the ATTACK action. - if (attacksReady.empty()) - return; - - Attack *highestPriorityAttack = 0; - // Performs all ready attacks. - for (std::vector<Attack *>::const_iterator it = attacksReady.begin(), - it_end = attacksReady.end(); it != it_end; ++it) - { - // check if target is in range using the pythagorean theorem - int distx = this->getPosition().x - mTarget->getPosition().x; - int disty = this->getPosition().y - mTarget->getPosition().y; - int distSquare = (distx * distx + disty * disty); - AttackInfo *info = (*it)->getAttackInfo(); - int maxDist = info->getDamage().range + getSize(); - - if (distSquare <= maxDist * maxDist && - (!highestPriorityAttack || - highestPriorityAttack->getAttackInfo()->getPriority() - < info->getPriority())) - { - highestPriorityAttack = *it; - } - } - if (highestPriorityAttack) - { - mAttacks.startAttack(highestPriorityAttack); - mCurrentAttack = highestPriorityAttack; - setDestination(getPosition()); - // TODO: Turn into direction of enemy - raiseUpdateFlags(UPDATEFLAG_ATTACK); - } + mDst = dst; + entity.getComponent<ActorComponent>()->raiseUpdateFlags( + UPDATEFLAG_NEW_DESTINATION); + mPath.clear(); } -void Being::addAttack(AttackInfo *attackInfo) +void BeingComponent::clearDestination(Entity &entity) { - mAttacks.add(attackInfo); + setDestination(entity, + entity.getComponent<ActorComponent>()->getPosition()); } -void Being::removeAttack(AttackInfo *attackInfo) +void BeingComponent::setDirection(Entity &entity, BeingDirection direction) { - mAttacks.remove(attackInfo); + mDirection = direction; + entity.getComponent<ActorComponent>()->raiseUpdateFlags( + UPDATEFLAG_DIRCHANGE); } -void Being::setDestination(const Point &dst) +Path BeingComponent::findPath(Entity &entity) { - mDst = dst; - raiseUpdateFlags(UPDATEFLAG_NEW_DESTINATION); - mPath.clear(); -} + auto *actorComponent = entity.getComponent<ActorComponent>(); -Path Being::findPath() -{ - Map *map = getMap()->getMap(); + Map *map = entity.getMap()->getMap(); int tileWidth = map->getTileWidth(); int tileHeight = map->getTileHeight(); - int startX = getPosition().x / tileWidth; - int startY = getPosition().y / tileHeight; + int startX = actorComponent->getPosition().x / tileWidth; + int startY = actorComponent->getPosition().y / tileHeight; int destX = mDst.x / tileWidth, destY = mDst.y / tileHeight; - return map->findPath(startX, startY, destX, destY, getWalkMask()); + return map->findPath(startX, startY, destX, destY, + actorComponent->getWalkMask()); } -void Being::updateDirection(const Point ¤tPos, const Point &destPos) +void BeingComponent::updateDirection(Entity &entity, + const Point ¤tPos, + const Point &destPos) { // We update the being direction on each tile to permit other beings // entering in range to always see the being with a direction value. @@ -291,18 +183,18 @@ void Being::updateDirection(const Point ¤tPos, const Point &destPos) if (currentPos.x == destPos.x) { if (currentPos.y > destPos.y) - setDirection(UP); + setDirection(entity, UP); else - setDirection(DOWN); + setDirection(entity, DOWN); return; } if (currentPos.y == destPos.y) { if (currentPos.x > destPos.x) - setDirection(LEFT); + setDirection(entity, LEFT); else - setDirection(RIGHT); + setDirection(entity, RIGHT); return; } @@ -316,9 +208,9 @@ void Being::updateDirection(const Point ¤tPos, const Point &destPos) // Compute tan of the angle if ((currentPos.y - destPos.y) / (destPos.x - currentPos.x) < 1) // The angle is less than 45°, we look to the right - setDirection(RIGHT); + setDirection(entity, RIGHT); else - setDirection(UP); + setDirection(entity, UP); return; } else // Down-right @@ -326,9 +218,9 @@ void Being::updateDirection(const Point ¤tPos, const Point &destPos) // Compute tan of the angle if ((destPos.y - currentPos.y) / (destPos.x - currentPos.x) < 1) // The angle is less than 45°, we look to the right - setDirection(RIGHT); + setDirection(entity, RIGHT); else - setDirection(DOWN); + setDirection(entity, DOWN); return; } } @@ -340,9 +232,9 @@ void Being::updateDirection(const Point ¤tPos, const Point &destPos) // Compute tan of the angle if ((currentPos.y - destPos.y) / (currentPos.x - destPos.x) < 1) // The angle is less than 45°, we look to the left - setDirection(LEFT); + setDirection(entity, LEFT); else - setDirection(UP); + setDirection(entity, UP); return; } else // Down-left @@ -350,15 +242,15 @@ void Being::updateDirection(const Point ¤tPos, const Point &destPos) // Compute tan of the angle if ((destPos.y - currentPos.y) / (currentPos.x - destPos.x) < 1) // The angle is less than 45°, we look to the left - setDirection(LEFT); + setDirection(entity, LEFT); else - setDirection(DOWN); + setDirection(entity, DOWN); return; } } } -void Being::move() +void BeingComponent::move(Entity &entity) { // Immobile beings cannot move. if (!checkAttributeExists(ATTR_MOVE_SPEED_RAW) @@ -368,10 +260,10 @@ void Being::move() // Remember the current position before moving. This is used by // MapComposite::update() to determine whether a being has moved from one // zone to another. - mOld = getPosition(); + mOld = entity.getComponent<ActorComponent>()->getPosition(); // Ignore not moving beings - if (mAction == STAND && mDst == getPosition()) + if (mAction == STAND && mDst == mOld) return; if (mMoveTime > WORLD_TICK_MS) @@ -381,22 +273,22 @@ void Being::move() return; } - Map *map = getMap()->getMap(); + Map *map = entity.getMap()->getMap(); int tileWidth = map->getTileWidth(); int tileHeight = map->getTileHeight(); - int tileSX = getPosition().x / tileWidth; - int tileSY = getPosition().y / tileHeight; + int tileSX = mOld.x / tileWidth; + int tileSY = mOld.y / tileHeight; int tileDX = mDst.x / tileWidth; int tileDY = mDst.y / tileHeight; if (tileSX == tileDX && tileSY == tileDY) { if (mAction == WALK) - setAction(STAND); + setAction(entity, STAND); // Moving while staying on the same tile is free // We only update the direction in that case. - updateDirection(getPosition(), mDst); - setPosition(mDst); + updateDirection(entity, mOld, mDst); + entity.getComponent<ActorComponent>()->setPosition(entity, mDst); mMoveTime = 0; return; } @@ -411,7 +303,9 @@ void Being::move() for (PathIterator pathIterator = mPath.begin(); pathIterator != mPath.end(); pathIterator++) { - if (!map->getWalk(pathIterator->x, pathIterator->y, getWalkMask())) + const unsigned char walkmask = + entity.getComponent<ActorComponent>()->getWalkMask(); + if (!map->getWalk(pathIterator->x, pathIterator->y, walkmask)) { mPath.clear(); break; @@ -422,20 +316,20 @@ void Being::move() { // No path exists: the walkability of cached path has changed, the // destination has changed, or a path was never set. - mPath = findPath(); + mPath = findPath(entity); } if (mPath.empty()) { if (mAction == WALK) - setAction(STAND); + setAction(entity, STAND); // no path was found mDst = mOld; mMoveTime = 0; return; } - setAction(WALK); + setAction(entity, WALK); Point prev(tileSX, tileSY); Point pos; @@ -460,15 +354,15 @@ void Being::move() pos.y = next.y * tileHeight + (tileHeight / 2); } while (mMoveTime < WORLD_TICK_MS); - setPosition(pos); + entity.getComponent<ActorComponent>()->setPosition(entity, pos); mMoveTime = mMoveTime > WORLD_TICK_MS ? mMoveTime - WORLD_TICK_MS : 0; // Update the being direction also - updateDirection(mOld, pos); + updateDirection(entity, mOld, pos); } -int Being::directionToAngle(int direction) +int BeingComponent::directionToAngle(int direction) { switch (direction) { @@ -480,54 +374,40 @@ int Being::directionToAngle(int direction) } } -int Being::performAttack(Being *target, const Damage &dmg) -{ - // check target legality - if (!target - || target == this - || target->getAction() == DEAD - || !target->canFight()) - return -1; - - if (getMap()->getPvP() == PVP_NONE - && target->getType() == OBJECT_CHARACTER - && getType() == OBJECT_CHARACTER) - return -1; - - return target->damage(this, dmg); -} - -void Being::setAction(BeingAction action) +void BeingComponent::setAction(Entity &entity, BeingAction action) { mAction = action; if (action != ATTACK && // The players are informed about these actions action != WALK) // by other messages { - raiseUpdateFlags(UPDATEFLAG_ACTIONCHANGE); + entity.getComponent<ActorComponent>()->raiseUpdateFlags( + UPDATEFLAG_ACTIONCHANGE); } } -void Being::applyModifier(unsigned attr, double value, unsigned layer, - unsigned duration, unsigned id) +void BeingComponent::applyModifier(Entity &entity, unsigned attr, double value, + unsigned layer, unsigned duration, + unsigned id) { mAttributes.at(attr).add(duration, value, layer, id); - updateDerivedAttributes(attr); + updateDerivedAttributes(entity, attr); } -bool Being::removeModifier(unsigned attr, double value, unsigned layer, - unsigned id, bool fullcheck) +bool BeingComponent::removeModifier(Entity &entity, unsigned attr, + double value, unsigned layer, + unsigned id, bool fullcheck) { bool ret = mAttributes.at(attr).remove(value, layer, id, fullcheck); - updateDerivedAttributes(attr); + updateDerivedAttributes(entity, attr); return ret; } -void Being::setGender(BeingGender gender) +void BeingComponent::setGender(BeingGender gender) { mGender = gender; } -void Being::setAttribute(unsigned id, double value) +void BeingComponent::setAttribute(Entity &entity, unsigned id, double value) { AttributeMap::iterator ret = mAttributes.find(id); if (ret == mAttributes.end()) @@ -543,16 +423,35 @@ void Being::setAttribute(unsigned id, double value) else { ret->second.setBase(value); - updateDerivedAttributes(id); + updateDerivedAttributes(entity, id); } } -double Being::getAttribute(unsigned id) const +void BeingComponent::createAttribute(unsigned id, const AttributeManager::AttributeInfo + &attributeInfo) +{ + mAttributes.insert(std::pair<unsigned, Attribute> + (id,Attribute(attributeInfo))); +} + +const Attribute *BeingComponent::getAttribute(unsigned id) const { AttributeMap::const_iterator ret = mAttributes.find(id); if (ret == mAttributes.end()) { - LOG_DEBUG("Being::getAttribute: Attribute " + LOG_DEBUG("BeingComponent::getAttribute: Attribute " + << id << " not found! Returning 0."); + return 0; + } + return &ret->second; +} + +double BeingComponent::getAttributeBase(unsigned id) const +{ + AttributeMap::const_iterator ret = mAttributes.find(id); + if (ret == mAttributes.end()) + { + LOG_DEBUG("BeingComponent::getAttributeBase: Attribute " << id << " not found! Returning 0."); return 0; } @@ -560,32 +459,32 @@ double Being::getAttribute(unsigned id) const } -double Being::getModifiedAttribute(unsigned id) const +double BeingComponent::getModifiedAttribute(unsigned id) const { AttributeMap::const_iterator ret = mAttributes.find(id); if (ret == mAttributes.end()) { - LOG_DEBUG("Being::getModifiedAttribute: Attribute " + LOG_DEBUG("BeingComponent::getModifiedAttribute: Attribute " << id << " not found! Returning 0."); return 0; } return ret->second.getModifiedAttribute(); } -void Being::setModAttribute(unsigned, double) +void BeingComponent::setModAttribute(unsigned, double) { // No-op to satisfy shared structure. // The game-server calculates this manually. return; } -void Being::recalculateBaseAttribute(unsigned attr) +void BeingComponent::recalculateBaseAttribute(Entity &entity, unsigned attr) { LOG_DEBUG("Being: Received update attribute recalculation request for " << attr << "."); if (!mAttributes.count(attr)) { - LOG_DEBUG("Being::recalculateBaseAttribute: " << attr << " not found!"); + LOG_DEBUG("BeingComponent::recalculateBaseAttribute: " << attr << " not found!"); return; } @@ -594,8 +493,8 @@ void Being::recalculateBaseAttribute(unsigned attr) { double newBase = utils::tpsToRawSpeed( getModifiedAttribute(ATTR_MOVE_SPEED_TPS)); - if (newBase != getAttribute(attr)) - setAttribute(attr, newBase); + if (newBase != getAttributeBase(attr)) + setAttribute(entity, attr, newBase); return; } @@ -604,13 +503,15 @@ void Being::recalculateBaseAttribute(unsigned attr) Script *script = ScriptManager::currentState(); script->prepare(mRecalculateBaseAttributeCallback); - script->push(this); + script->push(&entity); script->push(attr); - script->execute(getMap()); + script->execute(entity.getMap()); } -void Being::updateDerivedAttributes(unsigned attr) +void BeingComponent::updateDerivedAttributes(Entity &entity, unsigned attr) { + signal_attribute_changed.emit(&entity, attr); + LOG_DEBUG("Being: Updating derived attribute(s) of: " << attr); // Handle default actions before handing over to the script engine @@ -618,12 +519,13 @@ void Being::updateDerivedAttributes(unsigned attr) { case ATTR_MAX_HP: case ATTR_HP: - raiseUpdateFlags(UPDATEFLAG_HEALTHCHANGE); + entity.getComponent<ActorComponent>()->raiseUpdateFlags( + UPDATEFLAG_HEALTHCHANGE); break; case ATTR_MOVE_SPEED_TPS: // Does not make a lot of sense to have in the scripts. // So handle it here: - recalculateBaseAttribute(ATTR_MOVE_SPEED_RAW); + recalculateBaseAttribute(entity, ATTR_MOVE_SPEED_RAW); break; } @@ -632,12 +534,12 @@ void Being::updateDerivedAttributes(unsigned attr) Script *script = ScriptManager::currentState(); script->prepare(mRecalculateDerivedAttributesCallback); - script->push(this); + script->push(&entity); script->push(attr); - script->execute(getMap()); + script->execute(entity.getMap()); } -void Being::applyStatusEffect(int id, int timer) +void BeingComponent::applyStatusEffect(int id, int timer) { if (mAction == DEAD) return; @@ -655,12 +557,12 @@ void Being::applyStatusEffect(int id, int timer) } } -void Being::removeStatusEffect(int id) +void BeingComponent::removeStatusEffect(int id) { setStatusEffectTime(id, 0); } -bool Being::hasStatusEffect(int id) const +bool BeingComponent::hasStatusEffect(int id) const { StatusEffects::const_iterator it = mStatus.begin(); while (it != mStatus.end()) @@ -672,20 +574,20 @@ bool Being::hasStatusEffect(int id) const return false; } -unsigned Being::getStatusEffectTime(int id) const +unsigned BeingComponent::getStatusEffectTime(int id) const { StatusEffects::const_iterator it = mStatus.find(id); if (it != mStatus.end()) return it->second.time; else return 0; } -void Being::setStatusEffectTime(int id, int time) +void BeingComponent::setStatusEffectTime(int id, int time) { StatusEffects::iterator it = mStatus.find(id); if (it != mStatus.end()) it->second.time = time; } -void Being::update() +void BeingComponent::update(Entity &entity) { int oldHP = getModifiedAttribute(ATTR_HP); int newHP = oldHP; @@ -705,8 +607,9 @@ void Being::update() // Only update HP when it actually changed to avoid network noise if (newHP != oldHP) { - setAttribute(ATTR_HP, newHP); - raiseUpdateFlags(UPDATEFLAG_HEALTHCHANGE); + setAttribute(entity, ATTR_HP, newHP); + entity.getComponent<ActorComponent>()->raiseUpdateFlags( + UPDATEFLAG_HEALTHCHANGE); } // Update lifetime of effects. @@ -715,7 +618,7 @@ void Being::update() ++it) { if (it->second.tick()) - updateDerivedAttributes(it->first); + updateDerivedAttributes(entity, it->first); } // Update and run status effects @@ -724,7 +627,7 @@ void Being::update() { it->second.time--; if (it->second.time > 0 && mAction != DEAD) - it->second.status->tick(this, it->second.time); + it->second.status->tick(entity, it->second.time); if (it->second.time <= 0 || mAction == DEAD) { @@ -740,19 +643,13 @@ void Being::update() // Check if being died if (getModifiedAttribute(ATTR_HP) <= 0 && mAction != DEAD) - died(); - - processAttacks(); + died(entity); } -void Being::inserted(Entity *) +void BeingComponent::inserted(Entity *entity) { // Reset the old position, since after insertion it is important that it is // in sync with the zone that we're currently present in. - mOld = getPosition(); + mOld = entity->getComponent<ActorComponent>()->getPosition(); } -void Being::processAttack(Attack &attack) -{ - performAttack(mTarget, attack.getAttackInfo()->getDamage()); -} diff --git a/src/game-server/being.h b/src/game-server/being.h index 1d1f4204..5ccc8e89 100644 --- a/src/game-server/being.h +++ b/src/game-server/being.h @@ -32,7 +32,7 @@ #include "game-server/attack.h" #include "game-server/timeout.h" -class Being; +class BeingComponent; class MapComposite; class StatusEffect; @@ -47,59 +47,34 @@ struct Status typedef std::map< int, Status > StatusEffects; /** - * Type definition for a list of hits - */ -typedef std::vector<unsigned> Hits; - -/** * Generic being (living actor). Keeps direction, destination and a few other * relevant properties. Used for characters & monsters (all animated objects). */ -class Being : public Actor +class BeingComponent : public Component { public: + static const ComponentType type = CT_Being; + /** * Proxy constructor. */ - Being(EntityType type); + BeingComponent(Entity &entity); /** * Update being state. */ - virtual void update(); - - /** - * Takes a damage structure, computes the real damage based on the - * stats, deducts the result from the hitpoints and adds the result to - * the HitsTaken list. - */ - virtual int damage(Actor *source, const Damage &damage); + virtual void update(Entity &entity); /** Restores all hit points of the being */ - void heal(); + void heal(Entity &entity); /** Restores a specific number of hit points of the being */ - void heal(int hp); + void heal(Entity &entity, int hp); /** * Changes status and calls all the "died" listeners. */ - virtual void died(); - - /** - * Process all available attacks - */ - void processAttacks(); - - /** - * Adds an attack to the available attacks - */ - void addAttack(AttackInfo *attack); - - /** - * Removes an attack from the available attacks - */ - void removeAttack(AttackInfo *attackInfo); + virtual void died(Entity &entity); /** * Gets the destination coordinates of the being. @@ -110,14 +85,13 @@ class Being : public Actor /** * Sets the destination coordinates of the being. */ - void setDestination(const Point &dst); + void setDestination(Entity &entity, const Point &dst); /** * Sets the destination coordinates of the being to the current * position. */ - void clearDestination() - { setDestination(getPosition()); } + void clearDestination(Entity &entity); /** * Gets the old coordinates of the being. @@ -128,34 +102,14 @@ class Being : public Actor /** * Sets the facing direction of the being. */ - void setDirection(BeingDirection direction) - { mDirection = direction; raiseUpdateFlags(UPDATEFLAG_DIRCHANGE); } + void setDirection(Entity &entity, BeingDirection direction); BeingDirection getDirection() const { return mDirection; } - - /** - * Gets the damage list. - */ - const Hits &getHitsTaken() const - { return mHitsTaken; } - - /** - * Clears the damage list. - */ - void clearHitsTaken() - { mHitsTaken.clear(); } - - /** - * Performs an attack. - * Return Value: damage inflicted or -1 when illegal target - */ - int performAttack(Being *target, const Damage &dmg); - /** * Sets the current action. */ - void setAction(BeingAction action); + void setAction(Entity &entity, BeingAction action); /** * Sets the current action. @@ -164,23 +118,14 @@ class Being : public Actor { return mAction; } /** - * Gets the attack id the being is currently performing. - * For being, this is defaulted to the first one (1). - */ - virtual int getAttackId() const - { return mCurrentAttack ? - mCurrentAttack->getAttackInfo()->getDamage().id : 0; - } - - /** * Moves the being toward its destination. */ - void move(); + void move(Entity &entity); /** * Returns the path to the being's current destination. */ - virtual Path findPath(); + virtual Path findPath(Entity &); /** Gets the gender of the being (male or female). */ BeingGender getGender() const @@ -192,12 +137,29 @@ class Being : public Actor /** * Sets an attribute. */ - void setAttribute(unsigned id, double value); + void setAttribute(Entity &entity, unsigned id, double value); + + /** + * Creates an Attribute that did not exist before + * + * @param id The id of the attribute + * @param attributeInfo The info that describes the attribute + */ + void createAttribute(unsigned id, const AttributeManager::AttributeInfo + &attributeInfo); + + /** + * Gets an attribute or 0 if not existing. + */ + const Attribute *getAttribute(unsigned id) const; + + const AttributeMap &getAttributes() const + { return mAttributes; } /** - * Gets an attribute. + * Gets an attribute base. */ - double getAttribute(unsigned id) const; + double getAttributeBase(unsigned id) const; /** * Gets an attribute after applying modifiers. @@ -226,11 +188,13 @@ class Being : public Actor * @param lvl If non-zero, indicates that a temporary modifier can be * dispelled prematuraly by a spell of given level. */ - void applyModifier(unsigned attr, double value, unsigned layer, - unsigned duration = 0, unsigned id = 0); + void applyModifier(Entity &entity, unsigned attr, double value, + unsigned layer, unsigned duration = 0, + unsigned id = 0); - bool removeModifier(unsigned attr, double value, unsigned layer, - unsigned id = 0, bool fullcheck = false); + bool removeModifier(Entity &entity, unsigned attr, double value, + unsigned layer, unsigned id = 0, + bool fullcheck = false); /** * Called when an attribute modifier is changed. @@ -238,14 +202,14 @@ class Being : public Actor * attributes if it has changed. * @returns Whether it was changed. */ - virtual void recalculateBaseAttribute(unsigned); + void recalculateBaseAttribute(Entity &, unsigned); /** * Attribute has changed, recalculate base value of dependant * attributes (and handle other actions for the modified * attribute) */ - virtual void updateDerivedAttributes(unsigned); + void updateDerivedAttributes(Entity &entity, unsigned); /** * Sets a statuseffect on this being @@ -262,6 +226,9 @@ class Being : public Actor */ bool hasStatusEffect(int id) const; + const StatusEffects &getStatusEffects() const + { return mStatus; } + /** * Returns the time of the status effect if in effect, or 0 if not */ @@ -285,30 +252,19 @@ class Being : public Actor */ static int directionToAngle(int direction); - /** - * Get Target - */ - Being *getTarget() const - { return mTarget; } - - /** - * Set Target - */ - void setTarget(Being *target) - { mTarget = target; } - static void setUpdateDerivedAttributesCallback(Script *script) { script->assignCallback(mRecalculateDerivedAttributesCallback); } static void setRecalculateBaseAttributeCallback(Script *script) { script->assignCallback(mRecalculateBaseAttributeCallback); } - sigc::signal<void, Being *> signal_died; + sigc::signal<void, Entity *> signal_died; + sigc::signal<void, Entity *, unsigned> signal_attribute_changed; /** * Activate an emote flag on the being. */ - void triggerEmote(int id); + void triggerEmote(Entity &entity, int id); /** * Tells the last emote used. @@ -316,35 +272,29 @@ class Being : public Actor int getLastEmote() const { return mEmoteId; } - protected: - /** - * Performs an attack - */ - virtual void processAttack(Attack &attack); - /** * Update the being direction when moving so avoid directions desyncs * with other clients. */ - void updateDirection(const Point ¤tPos, + void updateDirection(Entity &entity, + const Point ¤tPos, const Point &destPos); + protected: static const int TICKS_PER_HP_REGENERATION = 100; + /** Delay until move to next tile in miliseconds. */ + unsigned short mMoveTime; BeingAction mAction; AttributeMap mAttributes; - Attacks mAttacks; StatusEffects mStatus; - Being *mTarget; Point mOld; /**< Old coordinates. */ Point mDst; /**< Target coordinates. */ BeingGender mGender; /**< Gender of the being. */ - Attack *mCurrentAttack; /**< Last used attack. */ - private: - Being(const Being &rhs); - Being &operator=(const Being &rhs); + BeingComponent(const BeingComponent &rhs); + BeingComponent &operator=(const BeingComponent &rhs); /** * Connected to signal_inserted to reset the old position. @@ -355,7 +305,6 @@ class Being : public Actor BeingDirection mDirection; /**< Facing direction. */ std::string mName; - Hits mHitsTaken; /**< List of punches taken since last update. */ /** Time until hp is regenerated again */ Timeout mHealthRegenerationTimeout; diff --git a/src/game-server/buysell.cpp b/src/game-server/buysell.cpp index 1a0d8d91..cc209ccf 100644 --- a/src/game-server/buysell.cpp +++ b/src/game-server/buysell.cpp @@ -30,15 +30,15 @@ #include <algorithm> -BuySell::BuySell(Character *c, bool sell): +BuySell::BuySell(Entity *c, bool sell): mCurrencyId(ATTR_GP), mChar(c), mSell(sell) { - c->setBuySell(this); + c->getComponent<CharacterComponent>()->setBuySell(this); } BuySell::~BuySell() { - mChar->setBuySell(NULL); + mChar->getComponent<CharacterComponent>()->setBuySell(nullptr); } void BuySell::cancel() @@ -46,7 +46,7 @@ void BuySell::cancel() delete this; } -bool BuySell::registerItem(int id, int amount, int cost) +bool BuySell::registerItem(unsigned id, int amount, int cost) { if (mSell) { @@ -57,8 +57,11 @@ bool BuySell::registerItem(int id, int amount, int cost) amount = nb; } - TradedItem it = { id, amount, cost }; - mItems.push_back(it); + TradedItem item; + item.itemId = id; + item.amount = amount; + item.cost = cost; + mItems.push_back(item); return true; } @@ -72,15 +75,17 @@ int BuySell::registerPlayerItems() // We parse the player inventory and add all item // in a sell list. - const InventoryData &inventoryData = mChar->getPossessions().getInventory(); + auto *component = mChar->getComponent<CharacterComponent>(); + const InventoryData &inventoryData = + component->getPossessions().getInventory(); for (InventoryData::const_iterator it = inventoryData.begin(), it_end = inventoryData.end(); it != it_end; ++it) { - unsigned nb = it->second.amount; - if (!nb) + unsigned amount = it->second.amount; + if (!amount) continue; - int id = it->second.itemId; + unsigned id = it->second.itemId; int cost = -1; if (itemManager->getItem(id)) { @@ -89,7 +94,8 @@ int BuySell::registerPlayerItems() else { LOG_WARN("registerPlayersItems(): The character Id: " - << mChar->getPublicID() << " has unknown items (Id: " << id + << mChar->getComponent<ActorComponent>()->getPublicID() + << " has unknown items (Id: " << id << "). They have been ignored."); continue; } @@ -106,22 +112,25 @@ int BuySell::registerPlayerItems() if (i->itemId == id) { itemAlreadyAdded = true; - i->amount += nb; + i->amount += amount; break; } } if (!itemAlreadyAdded) { - TradedItem itTrade = { id, nb, cost }; - mItems.push_back(itTrade); + TradedItem tradeItem; + tradeItem.itemId = id; + tradeItem.amount = amount; + tradeItem.cost = cost; + mItems.push_back(tradeItem); nbItemsToSell++; } } return nbItemsToSell; } -bool BuySell::start(Actor *actor) +bool BuySell::start(Entity *actor) { if (mItems.empty()) { @@ -130,7 +139,7 @@ bool BuySell::start(Actor *actor) } MessageOut msg(mSell ? GPMSG_NPC_SELL : GPMSG_NPC_BUY); - msg.writeInt16(actor->getPublicID()); + msg.writeInt16(actor->getComponent<ActorComponent>()->getPublicID()); for (TradedItems::const_iterator i = mItems.begin(), i_end = mItems.end(); i != i_end; ++i) { @@ -138,32 +147,38 @@ bool BuySell::start(Actor *actor) msg.writeInt16(i->amount); msg.writeInt16(i->cost); } - mChar->getClient()->send(msg); + mChar->getComponent<CharacterComponent>()->getClient()->send(msg); return true; } -void BuySell::perform(int id, int amount) +void BuySell::perform(unsigned id, int amount) { Inventory inv(mChar); for (TradedItems::iterator i = mItems.begin(), i_end = mItems.end(); i != i_end; ++i) { - if (i->itemId != id) continue; - if (i->amount && i->amount <= amount) amount = i->amount; + auto *beingComponent = mChar->getComponent<BeingComponent>(); + + if (i->itemId != id) + continue; + if (i->amount && i->amount <= amount) + amount = i->amount; if (mSell) { amount -= inv.remove(id, amount); - mChar->setAttribute(mCurrencyId, - mChar->getAttribute(mCurrencyId) + - amount * i->cost); + const double currentMoney = + beingComponent->getAttributeBase(mCurrencyId); + beingComponent->setAttribute(*mChar, mCurrencyId, + currentMoney + amount * i->cost); } else { - amount = std::min(amount, ((int) mChar->getAttribute(mCurrencyId)) / i->cost); + const double currentMoney = + beingComponent->getAttributeBase(mCurrencyId); + amount = std::min(amount, ((int)currentMoney) / i->cost); amount -= inv.insert(id, amount); - mChar->setAttribute(mCurrencyId, - mChar->getAttribute(mCurrencyId) - - amount * i->cost); + beingComponent->setAttribute(*mChar, mCurrencyId, + currentMoney - amount * i->cost); } if (i->amount) { diff --git a/src/game-server/buysell.h b/src/game-server/buysell.h index 38566005..0906aaf4 100644 --- a/src/game-server/buysell.h +++ b/src/game-server/buysell.h @@ -23,8 +23,7 @@ #include <vector> -class Character; -class Actor; +class Entity; class BuySell { @@ -33,7 +32,7 @@ class BuySell /** * Sets up a trade between a character and an NPC. */ - BuySell(Character *, bool sell); + BuySell(Entity *, bool sell); /** * Cancels the trade. @@ -45,7 +44,7 @@ class BuySell * and how much it will cost. * @return true if at least one item was registered. */ - bool registerItem(int id, int amount, int cost); + bool registerItem(unsigned id, int amount, int cost); /** * Registers every player's item at an average cost given by the ItemDB. @@ -57,12 +56,12 @@ class BuySell * Sends the item list to player. * @return true if at least one item was registered before start. */ - bool start(Actor *actor); + bool start(Entity *actor); /** * Performs the trade. */ - void perform(int id, int amount); + void perform(unsigned id, int amount); private: @@ -70,7 +69,9 @@ class BuySell struct TradedItem { - unsigned short itemId, amount, cost; + unsigned itemId; + int amount; + int cost; }; typedef std::vector< TradedItem > TradedItems; @@ -78,7 +79,7 @@ class BuySell /** The attribute ID of the currency to use. Hardcoded for now (FIXME) */ unsigned mCurrencyId; - Character *mChar; /**< Character involved. */ + Entity *mChar; /**< Character involved. */ TradedItems mItems; /**< Traded items. */ bool mSell; /**< Are items sold? */ }; diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp index 0f4d8837..dd99e3b1 100644 --- a/src/game-server/character.cpp +++ b/src/game-server/character.cpp @@ -22,8 +22,10 @@ #include "common/configuration.h" #include "game-server/accountconnection.h" +#include "game-server/attack.h" #include "game-server/attributemanager.h" #include "game-server/buysell.h" +#include "game-server/combatcomponent.h" #include "game-server/inventory.h" #include "game-server/item.h" #include "game-server/itemmanager.h" @@ -47,30 +49,29 @@ #include <limits.h> // Experience curve related values -const float Character::EXPCURVE_EXPONENT = 2.5f; -const float Character::EXPCURVE_FACTOR = 20.0f; -const float Character::LEVEL_SKILL_PRECEDENCE_FACTOR = 0.85f; -const float Character::EXP_LEVEL_FLEXIBILITY = 3.0f; +const float CharacterComponent::EXPCURVE_EXPONENT = 2.5f; +const float CharacterComponent::EXPCURVE_FACTOR = 20.0f; +const float CharacterComponent::LEVEL_SKILL_PRECEDENCE_FACTOR = 0.85f; +const float CharacterComponent::EXP_LEVEL_FLEXIBILITY = 3.0f; -Script::Ref Character::mDeathCallback; -Script::Ref Character::mDeathAcceptedCallback; -Script::Ref Character::mLoginCallback; +Script::Ref CharacterComponent::mDeathCallback; +Script::Ref CharacterComponent::mDeathAcceptedCallback; +Script::Ref CharacterComponent::mLoginCallback; -static bool executeCallback(Script::Ref function, Character *character) +static bool executeCallback(Script::Ref function, Entity &entity) { if (!function.isValid()) return false; Script *script = ScriptManager::currentState(); script->prepare(function); - script->push(character); - script->execute(character->getMap()); + script->push(&entity); + script->execute(entity.getMap()); return true; } -Character::Character(MessageIn &msg): - Being(OBJECT_CHARACTER), +CharacterComponent::CharacterComponent(Entity &entity, MessageIn &msg): mClient(NULL), mConnected(true), mTransactionHandler(NULL), @@ -86,29 +87,34 @@ Character::Character(MessageIn &msg): mTransaction(TRANS_NONE), mTalkNpcId(0), mNpcThread(0), - mKnuckleAttackInfo(0) + mKnuckleAttackInfo(0), + mBaseEntity(&entity) { - const AttributeManager::AttributeScope &attr = + auto *beingComponent = entity.getComponent<BeingComponent>(); + + const AttributeManager::AttributeScope &attributes = attributeManager->getAttributeScope(CharacterScope); LOG_DEBUG("Character creation: initialisation of " - << attr.size() << " attributes."); - for (AttributeManager::AttributeScope::const_iterator it1 = attr.begin(), - it1_end = attr.end(); it1 != it1_end; ++it1) - mAttributes.insert(std::make_pair(it1->first, Attribute(*it1->second))); + << attributes.size() << " attributes."); + for (auto attributeScope : attributes) + beingComponent->createAttribute(attributeScope.first, + *attributeScope.second); - setWalkMask(Map::BLOCKMASK_WALL); + auto *actorComponent = entity.getComponent<ActorComponent>(); + actorComponent->setWalkMask(Map::BLOCKMASK_WALL); + actorComponent->setBlockType(BLOCKTYPE_CHARACTER); + actorComponent->setSize(16); - // Get character data. - mDatabaseID = msg.readInt32(); - setName(msg.readString()); - deserializeCharacterData(*this, msg); - mOld = getPosition(); - Inventory(this).initialize(); - modifiedAllAttribute(); - setSize(16); + + CombatComponent *combatcomponent = new CombatComponent(entity); + entity.addComponent(combatcomponent); + combatcomponent->getAttacks().attack_added.connect( + sigc::mem_fun(this, &CharacterComponent::attackAdded)); + combatcomponent->getAttacks().attack_removed.connect( + sigc::mem_fun(this, &CharacterComponent::attackRemoved)); // Default knuckle attack - int damageBase = this->getModifiedAttribute(ATTR_STR); + int damageBase = beingComponent->getModifiedAttribute(ATTR_STR); int damageDelta = damageBase / 2; Damage knuckleDamage; knuckleDamage.skill = skillManager->getDefaultSkillId(); @@ -120,29 +126,39 @@ Character::Character(MessageIn &msg): knuckleDamage.range = DEFAULT_TILE_LENGTH; mKnuckleAttackInfo = new AttackInfo(0, knuckleDamage, 7, 3, 0); - addAttack(mKnuckleAttackInfo); + combatcomponent->addAttack(mKnuckleAttackInfo); + + // Get character data. + mDatabaseID = msg.readInt32(); + beingComponent->setName(msg.readString()); + + CharacterData characterData(&entity, this); + deserializeCharacterData(characterData, msg); + + Inventory(&entity, mPossessions).initialize(); + modifiedAllAttributes(entity);; + + beingComponent->signal_attribute_changed.connect(sigc::mem_fun( + this, &CharacterComponent::attributeChanged)); } -Character::~Character() +CharacterComponent::~CharacterComponent() { delete mNpcThread; delete mKnuckleAttackInfo; } -void Character::update() +void CharacterComponent::update(Entity &entity) { - // First, deal with being generic updates - Being::update(); - // Update character level if needed. if (mRecalculateLevel) { mRecalculateLevel = false; - recalculateLevel(); + recalculateLevel(entity); } // Dead character: don't regenerate anything else - if (getAction() == DEAD) + if (entity.getComponent<BeingComponent>()->getAction() == DEAD) return; // Update special recharge @@ -158,9 +174,9 @@ void Character::update() { Script *script = ScriptManager::currentState(); script->prepare(s.specialInfo->rechargedCallback); - script->push(this); + script->push(&entity); script->push(s.specialInfo->id); - script->execute(getMap()); + script->execute(entity.getMap()); } } } @@ -170,52 +186,46 @@ void Character::update() sendSpecialUpdate(); mSpecialUpdateNeeded = false; } - - mStatusEffects.clear(); - StatusEffects::iterator it = mStatus.begin(); - while (it != mStatus.end()) - { - mStatusEffects[it->first] = it->second.time; - it++; - } } -void Character::died() +void CharacterComponent::characterDied(Entity *being) { - Being::died(); - executeCallback(mDeathCallback, this); + executeCallback(mDeathCallback, *being); } -void Character::respawn() +void CharacterComponent::respawn(Entity &entity) { - if (mAction != DEAD) + auto *beingComponent = entity.getComponent<BeingComponent>(); + + if (beingComponent->getAction() != DEAD) { - LOG_WARN("Character \"" << getName() + LOG_WARN("Character \"" << beingComponent->getName() << "\" tried to respawn without being dead"); return; } // Make it alive again - setAction(STAND); + beingComponent->setAction(entity, STAND); // Reset target - mTarget = NULL; + entity.getComponent<CombatComponent>()->clearTarget(); // Execute respawn callback when set - if (executeCallback(mDeathAcceptedCallback, this)) + if (executeCallback(mDeathAcceptedCallback, entity)) return; // No script respawn callback set - fall back to hardcoded logic - mAttributes[ATTR_HP].setBase(mAttributes[ATTR_MAX_HP].getModifiedAttribute()); - updateDerivedAttributes(ATTR_HP); + const double maxHp = beingComponent->getModifiedAttribute(ATTR_MAX_HP); + beingComponent->setAttribute(entity, ATTR_HP, maxHp); // Warp back to spawn point. int spawnMap = Configuration::getValue("char_respawnMap", 1); int spawnX = Configuration::getValue("char_respawnX", 1024); int spawnY = Configuration::getValue("char_respawnY", 1024); - GameState::enqueueWarp(this, MapManager::getMap(spawnMap), spawnX, spawnY); + GameState::enqueueWarp(&entity, MapManager::getMap(spawnMap), + Point(spawnX, spawnY)); } -bool Character::specialUseCheck(SpecialMap::iterator it) +bool CharacterComponent::specialUseCheck(SpecialMap::iterator it) { if (it == mSpecials.end()) { @@ -245,7 +255,7 @@ bool Character::specialUseCheck(SpecialMap::iterator it) return true; } -void Character::useSpecialOnBeing(int id, Being *b) +void CharacterComponent::useSpecialOnBeing(Entity &user, int id, Entity *b) { SpecialMap::iterator it = mSpecials.find(id); if (!specialUseCheck(it)) @@ -258,13 +268,13 @@ void Character::useSpecialOnBeing(int id, Being *b) //tell script engine to cast the spell Script *script = ScriptManager::currentState(); script->prepare(special.specialInfo->useCallback); - script->push(this); + script->push(&user); script->push(b); script->push(special.specialInfo->id); - script->execute(getMap()); + script->execute(user.getMap()); } -void Character::useSpecialOnPoint(int id, int x, int y) +void CharacterComponent::useSpecialOnPoint(Entity &user, int id, int x, int y) { SpecialMap::iterator it = mSpecials.find(id); if (!specialUseCheck(it)) @@ -277,14 +287,14 @@ void Character::useSpecialOnPoint(int id, int x, int y) //tell script engine to cast the spell Script *script = ScriptManager::currentState(); script->prepare(special.specialInfo->useCallback); - script->push(this); + script->push(&user); script->push(x); script->push(y); script->push(special.specialInfo->id); - script->execute(getMap()); + script->execute(user.getMap()); } -bool Character::giveSpecial(int id, int currentMana) +bool CharacterComponent::giveSpecial(int id, int currentMana) { if (mSpecials.find(id) == mSpecials.end()) { @@ -303,7 +313,7 @@ bool Character::giveSpecial(int id, int currentMana) return false; } -bool Character::setSpecialMana(int id, int mana) +bool CharacterComponent::setSpecialMana(int id, int mana) { SpecialMap::iterator it = mSpecials.find(id); if (it != mSpecials.end()) @@ -315,7 +325,7 @@ bool Character::setSpecialMana(int id, int mana) return false; } -bool Character::setSpecialRechargeSpeed(int id, int speed) +bool CharacterComponent::setSpecialRechargeSpeed(int id, int speed) { SpecialMap::iterator it = mSpecials.find(id); if (it != mSpecials.end()) @@ -327,7 +337,7 @@ bool Character::setSpecialRechargeSpeed(int id, int speed) return false; } -void Character::sendSpecialUpdate() +void CharacterComponent::sendSpecialUpdate() { //GPMSG_SPECIAL_STATUS = 0x0293, // { B specialID, L current, L max, L recharge } @@ -341,20 +351,10 @@ void Character::sendSpecialUpdate() msg.writeInt32(it->second.specialInfo->neededMana); msg.writeInt32(it->second.rechargeSpeed); } - gameHandler->sendTo(this, msg); -} - -int Character::getMapId() const -{ - return getMap()->getID(); + gameHandler->sendTo(mClient, msg); } -void Character::setMapId(int id) -{ - setMap(MapManager::getMap(id)); -} - -void Character::cancelTransaction() +void CharacterComponent::cancelTransaction() { TransactionType t = mTransaction; mTransaction = TRANS_NONE; @@ -371,19 +371,19 @@ void Character::cancelTransaction() } } -Trade *Character::getTrading() const +Trade *CharacterComponent::getTrading() const { return mTransaction == TRANS_TRADE ? static_cast< Trade * >(mTransactionHandler) : NULL; } -BuySell *Character::getBuySell() const +BuySell *CharacterComponent::getBuySell() const { return mTransaction == TRANS_BUYSELL ? static_cast< BuySell * >(mTransactionHandler) : NULL; } -void Character::setTrading(Trade *t) +void CharacterComponent::setTrading(Trade *t) { if (t) { @@ -398,7 +398,7 @@ void Character::setTrading(Trade *t) } } -void Character::setBuySell(BuySell *t) +void CharacterComponent::setBuySell(BuySell *t) { if (t) { @@ -413,18 +413,19 @@ void Character::setBuySell(BuySell *t) } } -void Character::sendStatus() +void CharacterComponent::sendStatus(Entity &entity) { + auto *beingComponent = entity.getComponent<BeingComponent>(); MessageOut attribMsg(GPMSG_PLAYER_ATTRIBUTE_CHANGE); for (std::set<size_t>::const_iterator i = mModifiedAttributes.begin(), i_end = mModifiedAttributes.end(); i != i_end; ++i) { int attr = *i; attribMsg.writeInt16(attr); - attribMsg.writeInt32(getAttribute(attr) * 256); - attribMsg.writeInt32(getModifiedAttribute(attr) * 256); + attribMsg.writeInt32(beingComponent->getAttributeBase(attr) * 256); + attribMsg.writeInt32(beingComponent->getModifiedAttribute(attr) * 256); } - if (attribMsg.getLength() > 2) gameHandler->sendTo(this, attribMsg); + if (attribMsg.getLength() > 2) gameHandler->sendTo(mClient, attribMsg); mModifiedAttributes.clear(); MessageOut expMsg(GPMSG_PLAYER_EXP_CHANGE); @@ -437,7 +438,7 @@ void Character::sendStatus() expMsg.writeInt32(getExpNeeded(skill)); expMsg.writeInt16(levelForExp(getExperience(skill))); } - if (expMsg.getLength() > 2) gameHandler->sendTo(this, expMsg); + if (expMsg.getLength() > 2) gameHandler->sendTo(mClient, expMsg); mModifiedExperience.clear(); if (mUpdateLevelProgress) @@ -445,73 +446,53 @@ void Character::sendStatus() mUpdateLevelProgress = false; MessageOut progressMessage(GPMSG_LEVEL_PROGRESS); progressMessage.writeInt8(mLevelProgress); - gameHandler->sendTo(this, progressMessage); + gameHandler->sendTo(mClient, progressMessage); } } -void Character::modifiedAllAttribute() +void CharacterComponent::modifiedAllAttributes(Entity &entity) { + auto *beingComponent = entity.getComponent<BeingComponent>(); + LOG_DEBUG("Marking all attributes as changed, requiring recalculation."); - for (AttributeMap::iterator it = mAttributes.begin(), - it_end = mAttributes.end(); - it != it_end; ++it) + for (auto attribute : beingComponent->getAttributes()) { - recalculateBaseAttribute(it->first); - updateDerivedAttributes(it->first); + beingComponent->recalculateBaseAttribute(entity, attribute.first); + mModifiedAttributes.insert(attribute.first); } } -void Character::recalculateBaseAttribute(unsigned attr) +void CharacterComponent::attributeChanged(Entity *entity, unsigned attr) { - // `attr' may or may not have changed. Recalculate the base value. - LOG_DEBUG("Received update attribute recalculation request at Character " - "for " << attr << "."); - if (!mAttributes.count(attr)) - return; + auto *beingComponent = entity->getComponent<BeingComponent>(); + // Inform the client of this attribute modification. + accountHandler->updateAttributes(getDatabaseID(), attr, + beingComponent->getAttributeBase(attr), + beingComponent->getModifiedAttribute(attr)); + mModifiedAttributes.insert(attr); + + // Update the knuckle Attack if required if (attr == ATTR_STR && mKnuckleAttackInfo) { // TODO: dehardcode this Damage &knuckleDamage = mKnuckleAttackInfo->getDamage(); - knuckleDamage.base = getModifiedAttribute(ATTR_STR); + knuckleDamage.base = beingComponent->getModifiedAttribute(ATTR_STR); knuckleDamage.delta = knuckleDamage.base / 2; } - Being::recalculateBaseAttribute(attr); - -} - - -void Character::updateDerivedAttributes(unsigned attr) -{ - /* - * `attr' has changed, perform updates accordingly. - */ - flagAttribute(attr); - - - Being::updateDerivedAttributes(attr); } -void Character::flagAttribute(int attr) -{ - // Inform the client of this attribute modification. - accountHandler->updateAttributes(getDatabaseID(), attr, - getAttribute(attr), - getModifiedAttribute(attr)); - mModifiedAttributes.insert(attr); -} - -int Character::expForLevel(int level) +int CharacterComponent::expForLevel(int level) { return int(pow(level, EXPCURVE_EXPONENT) * EXPCURVE_FACTOR); } -int Character::levelForExp(int exp) +int CharacterComponent::levelForExp(int exp) { return int(pow(float(exp) / EXPCURVE_FACTOR, 1.0f / EXPCURVE_EXPONENT)); } -void Character::receiveExperience(int skill, int experience, int optimalLevel) +void CharacterComponent::receiveExperience(int skill, int experience, int optimalLevel) { // reduce experience when skill is over optimal level int levelOverOptimum = levelForExp(getExperience(skill)) - optimalLevel; @@ -550,7 +531,7 @@ void Character::receiveExperience(int skill, int experience, int optimalLevel) mRecalculateLevel = true; } -void Character::incrementKillCount(int monsterType) +void CharacterComponent::incrementKillCount(int monsterType) { std::map<int, int>::iterator i = mKillCount.find(monsterType); if (i == mKillCount.end()) @@ -565,7 +546,7 @@ void Character::incrementKillCount(int monsterType) } } -int Character::getKillCount(int monsterType) const +int CharacterComponent::getKillCount(int monsterType) const { std::map<int, int>::const_iterator i = mKillCount.find(monsterType); if (i != mKillCount.end()) @@ -574,7 +555,7 @@ int Character::getKillCount(int monsterType) const } -void Character::recalculateLevel() +void CharacterComponent::recalculateLevel(Entity &entity) { std::list<float> levels; std::map<int, int>::const_iterator a; @@ -606,7 +587,7 @@ void Character::recalculateLevel() while (mLevel < level) { - levelup(); + levelup(entity); } int levelProgress = int((level - floor(level)) * 100); @@ -617,19 +598,19 @@ void Character::recalculateLevel() } } -int Character::getExpNeeded(size_t skill) const +int CharacterComponent::getExpNeeded(size_t skill) const { int level = levelForExp(getExperience(skill)); - return Character::expForLevel(level + 1) - expForLevel(level); + return CharacterComponent::expForLevel(level + 1) - expForLevel(level); } -int Character::getExpGot(size_t skill) const +int CharacterComponent::getExpGot(size_t skill) const { int level = levelForExp(getExperience(skill)); - return mExperience.at(skill) - Character::expForLevel(level); + return mExperience.at(skill) - CharacterComponent::expForLevel(level); } -void Character::levelup() +void CharacterComponent::levelup(Entity &entity) { mLevel++; @@ -642,48 +623,61 @@ void Character::levelup() levelupMsg.writeInt16(mLevel); levelupMsg.writeInt16(mCharacterPoints); levelupMsg.writeInt16(mCorrectionPoints); - gameHandler->sendTo(this, levelupMsg); - LOG_INFO(getName()<<" reached level "<<mLevel); + gameHandler->sendTo(mClient, levelupMsg); + LOG_INFO(entity.getComponent<BeingComponent>()->getName() + << " reached level " << mLevel); } -AttribmodResponseCode Character::useCharacterPoint(size_t attribute) +AttribmodResponseCode CharacterComponent::useCharacterPoint(Entity &entity, + size_t attribute) { + auto *beingComponent = entity.getComponent<BeingComponent>(); + if (!attributeManager->isAttributeDirectlyModifiable(attribute)) return ATTRIBMOD_INVALID_ATTRIBUTE; if (!mCharacterPoints) return ATTRIBMOD_NO_POINTS_LEFT; --mCharacterPoints; - setAttribute(attribute, getAttribute(attribute) + 1); - updateDerivedAttributes(attribute); + + const double base = beingComponent->getAttributeBase(attribute); + beingComponent->setAttribute(entity, attribute, base + 1); + beingComponent->updateDerivedAttributes(entity, attribute); return ATTRIBMOD_OK; } -AttribmodResponseCode Character::useCorrectionPoint(size_t attribute) +AttribmodResponseCode CharacterComponent::useCorrectionPoint(Entity &entity, + size_t attribute) { + auto *beingComponent = entity.getComponent<BeingComponent>(); + if (!attributeManager->isAttributeDirectlyModifiable(attribute)) return ATTRIBMOD_INVALID_ATTRIBUTE; if (!mCorrectionPoints) return ATTRIBMOD_NO_POINTS_LEFT; - if (getAttribute(attribute) <= 1) + if (beingComponent->getAttributeBase(attribute) <= 1) return ATTRIBMOD_DENIED; --mCorrectionPoints; ++mCharacterPoints; - setAttribute(attribute, getAttribute(attribute) - 1); - updateDerivedAttributes(attribute); + + const double base = beingComponent->getAttributeBase(attribute); + beingComponent->setAttribute(entity, attribute, base - 1); return ATTRIBMOD_OK; } -void Character::startNpcThread(Script::Thread *thread, int npcId) +void CharacterComponent::startNpcThread(Script::Thread *thread, int npcId) { + if (mNpcThread) + delete mNpcThread; + mNpcThread = thread; mTalkNpcId = npcId; resumeNpcThread(); } -void Character::resumeNpcThread() +void CharacterComponent::resumeNpcThread() { Script *script = ScriptManager::currentState(); @@ -693,42 +687,44 @@ void Character::resumeNpcThread() { MessageOut msg(GPMSG_NPC_CLOSE); msg.writeInt16(mTalkNpcId); - gameHandler->sendTo(this, msg); + gameHandler->sendTo(mClient, msg); mTalkNpcId = 0; mNpcThread = 0; } } -void Character::addAttack(AttackInfo *attackInfo) +void CharacterComponent::attackAdded(CombatComponent *combatComponent, + Attack &attack) { // Remove knuckle attack - Being::addAttack(attackInfo); - Being::removeAttack(mKnuckleAttackInfo); + if (attack.getAttackInfo() != mKnuckleAttackInfo) + combatComponent->removeAttack(mKnuckleAttackInfo); } -void Character::removeAttack(AttackInfo *attackInfo) +void CharacterComponent::attackRemoved(CombatComponent *combatComponent, + Attack &attack) { - Being::removeAttack(attackInfo); // Add knuckle attack - if (mAttacks.getNumber() == 0) - Being::addAttack(mKnuckleAttackInfo); + // 1 since the attack is not really removed yet. + if (combatComponent->getAttacks().getNumber() == 1) + combatComponent->addAttack(mKnuckleAttackInfo); } -void Character::disconnected() +void CharacterComponent::disconnected(Entity &entity) { mConnected = false; // Make the dead characters respawn, even in case of disconnection. - if (getAction() == DEAD) - respawn(); + if (entity.getComponent<BeingComponent>()->getAction() == DEAD) + respawn(entity); else - GameState::remove(this); + GameState::remove(&entity); - signal_disconnected.emit(this); + signal_disconnected.emit(entity); } -bool Character::takeSpecial(int id) +bool CharacterComponent::takeSpecial(int id) { SpecialMap::iterator i = mSpecials.find(id); if (i != mSpecials.end()) @@ -740,12 +736,12 @@ bool Character::takeSpecial(int id) return false; } -void Character::clearSpecials() +void CharacterComponent::clearSpecials() { mSpecials.clear(); } -void Character::triggerLoginCallback() +void CharacterComponent::triggerLoginCallback(Entity &entity) { - executeCallback(mLoginCallback, this); + executeCallback(mLoginCallback, entity); } diff --git a/src/game-server/character.h b/src/game-server/character.h index 830504c7..a6ea6db4 100644 --- a/src/game-server/character.h +++ b/src/game-server/character.h @@ -26,6 +26,8 @@ #include "common/manaserv_protocol.h" #include "game-server/being.h" +#include "game-server/mapcomposite.h" +#include "game-server/mapmanager.h" #include "game-server/specialmanager.h" #include "scripting/script.h" @@ -33,6 +35,7 @@ #include "utils/logger.h" #include <map> +#include <set> #include <string> #include <vector> @@ -62,48 +65,113 @@ struct SpecialValue */ typedef std::map<unsigned, SpecialValue> SpecialMap; + +class CharacterData +{ +public: + CharacterData(Entity *entity, CharacterComponent *characterComponent); + + void setGender(BeingGender); + BeingGender getGender() const; + + void setMapId(int id); + int getMapId() const; + void setPosition(Point &point); + const Point &getPosition() const; + + void setAttribute(int id, int base); + void setModAttribute(int id, int mod); + const AttributeMap &getAttributes() const; + void setCharacterPoints(int characterPoints); + int getCharacterPoints() const; + void setCorrectionPoints(int correctionPoints); + int getCorrectionPoints() const; + + void setExperience(int skill, int level); + void setLevel(int level) const; + int getLevel() const; + + int getAccountLevel() const; + + void setHairStyle(int style); + int getHairStyle() const; + void setHairColor(int color); + int getHairColor() const; + + void setAccountLevel(int level); + + int getSkillSize() const; + const std::map<int, int>::const_iterator getSkillBegin() const; + const std::map<int, int>::const_iterator getSkillEnd() const; + + void applyStatusEffect(int status, int time); + int getStatusEffectSize() const; + const std::map<int, Status>::const_iterator getStatusEffectBegin() const; + const std::map<int, Status>::const_iterator getStatusEffectEnd() const; + + int getKillCountSize() const; + const std::map<int, int>::const_iterator getKillCountBegin() const; + const std::map<int, int>::const_iterator getKillCountEnd() const; + void setKillCount(int monsterId, int kills); + + void clearSpecials(); + void giveSpecial(int id, int mana); + int getSpecialSize() const; + SpecialMap::const_iterator getSpecialBegin() const; + SpecialMap::const_iterator getSpecialEnd() const; + + Possessions &getPossessions() const; + +private: + Entity *mEntity; + CharacterComponent *mCharacterComponent; +}; + + /** * The representation of a player's character in the game world. */ -class Character : public Being +class CharacterComponent : public Component { public: + static const ComponentType type = CT_Character; + /** * Utility constructor for creating a Character from a received * characterdata message. */ - Character(MessageIn &msg); + CharacterComponent(Entity &entity, MessageIn &msg); - ~Character(); + ~CharacterComponent(); /** * recalculates the level when necessary and calls Being::update */ - void update(); + void update(Entity &entity); void processAttacks(); /** - * Executes the global die script and calls the base class function + * Executes the global die script */ - virtual void died(); + virtual void characterDied(Entity *); /** * makes the character respawn */ - void respawn(); + void respawn(Entity &entity); /** * makes the character perform a special action on a being * when it is allowed to do so */ - void useSpecialOnBeing(int id, Being *b); + void useSpecialOnBeing(Entity &user, int id, Entity *b); /** * makes the character perform a special action on a map point * when it is allowed to do so */ - void useSpecialOnPoint(int id, int x, int y); + void useSpecialOnPoint(Entity &user, int id, int x, int y); /** * Allows a character to perform a special action @@ -231,43 +299,25 @@ class Character : public Being * Sends a message that informs the client about attribute * modified since last call. */ - void sendStatus(); - - /** - * Gets the ID of the map that the character is on. - * For serialization purpose only. - */ - int getMapId() const; - - /** - * Sets the ID of the map that the character is on. - * For serialization purpose only. - */ - void setMapId(int); + void sendStatus(Entity &entity); /** * Marks all attributes as being modified. */ - void modifiedAllAttribute(); - - /** - * Recalculate the base value of an attribute and update derived - * attributes if it has changed. - */ - void recalculateBaseAttribute(unsigned); - + void modifiedAllAttributes(Entity &entity); /** - * Attribute has changed, recalculate base value of dependant - * attributes (and handle other actions for the modified - * attribute) + * Signal handler for attribute changed event + * Flags the attribute as modified. + * @param being th being of which the attribute was changed + * @param attributeId the changed id */ - void updateDerivedAttributes(unsigned); + void attributeChanged(Entity *being, unsigned attributeId); /** * Calls all the "disconnected" listener. */ - void disconnected(); + void disconnected(Entity &entity); /** * Associative array containing all the quest variables known by the @@ -291,18 +341,6 @@ class Character : public Being { return mExperience.end(); } /** - * Used to serialize status effects. - */ - int getStatusEffectSize() const - { return mStatusEffects.size(); } - - const std::map<int, int>::const_iterator getStatusEffectBegin() const - { return mStatusEffects.begin(); } - - const std::map<int, int>::const_iterator getStatusEffectEnd() const - { return mStatusEffects.end(); } - - /** * Used to serialize kill count. */ int getKillCountSize() const @@ -365,13 +403,15 @@ class Character : public Being * Tries to use a character point to increase a * basic attribute */ - AttribmodResponseCode useCharacterPoint(size_t attribute); + AttribmodResponseCode useCharacterPoint(Entity &entity, + size_t attribute); /** * Tries to use a correction point to reduce a * basic attribute and regain a character point */ - AttribmodResponseCode useCorrectionPoint(size_t attribute); + AttribmodResponseCode useCorrectionPoint(Entity &entity, + size_t attribute); void setCharacterPoints(int points) { mCharacterPoints = points; } int getCharacterPoints() const { return mCharacterPoints; } @@ -422,20 +462,12 @@ class Character : public Being static void setLoginCallback(Script *script) { script->assignCallback(mLoginCallback); } - void triggerLoginCallback(); - - virtual void addAttack(AttackInfo *attackInfo); + void triggerLoginCallback(Entity &entity); - virtual void removeAttack(AttackInfo *attackInfo); + void attackAdded(CombatComponent *combatComponent, Attack &attackInfo); + void attackRemoved(CombatComponent *combatComponent, Attack &attackInfo); - sigc::signal<void, Character *> signal_disconnected; - - protected: - /** - * Gets the way the actor blocks pathfinding for other objects - */ - virtual BlockType getBlockType() const - { return BLOCKTYPE_CHARACTER; } + sigc::signal<void, Entity &> signal_disconnected; private: @@ -446,8 +478,8 @@ class Character : public Being double getAttrMod(AttributeMap::const_iterator it) const { return it->second.getModifiedAttribute(); } - Character(const Character &); - Character &operator=(const Character &); + CharacterComponent(const CharacterComponent &); + CharacterComponent &operator=(const CharacterComponent &); static const float EXPCURVE_EXPONENT; static const float EXPCURVE_FACTOR; @@ -460,12 +492,7 @@ class Character : public Being /** * Advances the character by one level; */ - void levelup(); - - /** - * Marks attribute as recently modified. - */ - void flagAttribute(int); + void levelup(Entity &entity); /** * Returns the exp needed for next skill levelup @@ -480,7 +507,7 @@ class Character : public Being /** * Recalculates the character level */ - void recalculateLevel(); + void recalculateLevel(Entity &entity); /** * Informs the client about his characters special charge status @@ -510,9 +537,6 @@ class Character : public Being std::map<int, int> mExperience; /**< experience collected for each skill.*/ 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*/ bool mSpecialUpdateNeeded; int mDatabaseID; /**< Character's database ID. */ @@ -536,13 +560,219 @@ class Character : public Being AttackInfo *mKnuckleAttackInfo; + Entity *mBaseEntity; /**< The entity this component is part of + this is ONLY required to allow using + the serialization routine without many + changes (we cannot pass the entity as + argument there). DO NOT USE THIS IF IT + IS AVOIDABLE in order to allow + refactoring this easier later! */ + static Script::Ref mDeathCallback; static Script::Ref mDeathAcceptedCallback; static Script::Ref mLoginCallback; - - // Set as a friend, but still a lot of redundant accessors. FIXME. - template< class T > - friend void serializeCharacterData(const T &data, MessageOut &msg); }; + +inline CharacterData::CharacterData(Entity *entity, + CharacterComponent *characterComponent): + mEntity(entity), + mCharacterComponent(characterComponent) +{} + +inline void CharacterData::setGender(BeingGender gender) +{ + mEntity->getComponent<BeingComponent>()->setGender(gender); +} + +inline BeingGender CharacterData::getGender() const +{ + return mEntity->getComponent<BeingComponent>()->getGender(); +} + +inline void CharacterData::setMapId(int id) +{ + mEntity->setMap(MapManager::getMap(id)); +} + +inline int CharacterData::getMapId() const +{ + return mEntity->getMap()->getID(); +} + +inline void CharacterData::setPosition(Point &point) +{ + mEntity->getComponent<ActorComponent>()->setPosition(*mEntity, point); +} + +inline const Point &CharacterData::getPosition() const +{ + return mEntity->getComponent<ActorComponent>()->getPosition(); +} + +inline void CharacterData::setAttribute(int id, int base) +{ + mEntity->getComponent<BeingComponent>()->setAttribute(*mEntity, id, base); +} + +inline void CharacterData::setModAttribute(int id, int mod) +{ + mEntity->getComponent<BeingComponent>()->setModAttribute(id, mod); +} + +inline const AttributeMap &CharacterData::getAttributes() const +{ + return mEntity->getComponent<BeingComponent>()->getAttributes(); +} + +inline void CharacterData::setCharacterPoints(int characterPoints) +{ + mCharacterComponent->setCharacterPoints(characterPoints); +} + +inline int CharacterData::getCharacterPoints() const +{ + return mCharacterComponent->getCharacterPoints(); +} + +inline void CharacterData::setCorrectionPoints(int correctionPoints) +{ + mCharacterComponent->setCorrectionPoints(correctionPoints); +} + +inline int CharacterData::getCorrectionPoints() const +{ + return mCharacterComponent->getCorrectionPoints(); +} + +inline void CharacterData::setExperience(int skill, int level) +{ + mCharacterComponent->setExperience(skill, level); +} + +inline void CharacterData::setLevel(int level) const +{ + mCharacterComponent->setLevel(level); +} + +inline int CharacterData::getLevel() const +{ + return mCharacterComponent->getLevel(); +} + +inline int CharacterData::getAccountLevel() const +{ + return mCharacterComponent->getAccountLevel(); +} + +inline void CharacterData::setHairStyle(int style) +{ + mCharacterComponent->setHairStyle(style); +} + +inline int CharacterData::getHairStyle() const +{ + return mCharacterComponent->getHairStyle(); +} + +inline void CharacterData::setHairColor(int color) +{ + mCharacterComponent->setHairColor(color); +} + +inline int CharacterData::getHairColor() const +{ + return mCharacterComponent->getHairColor(); +} + +inline void CharacterData::setAccountLevel(int level) +{ + mCharacterComponent->setAccountLevel(level); +} + +inline int CharacterData::getSkillSize() const +{ + return mCharacterComponent->getSkillSize(); +} + +inline const std::map<int, int>::const_iterator CharacterData::getSkillBegin() const +{ + return mCharacterComponent->getSkillBegin(); +} + +inline const std::map<int, int>::const_iterator CharacterData::getSkillEnd() const +{ + return mCharacterComponent->getSkillEnd(); +} + +inline void CharacterData::applyStatusEffect(int status, int time) +{ + mEntity->getComponent<BeingComponent>()->applyStatusEffect(status, time); +} + +inline int CharacterData::getStatusEffectSize() const +{ + return mEntity->getComponent<BeingComponent>()->getStatusEffects().size(); +} + +inline const std::map<int, Status>::const_iterator CharacterData::getStatusEffectBegin() const +{ + return mEntity->getComponent<BeingComponent>()->getStatusEffects().begin(); +} + +inline const std::map<int, Status>::const_iterator CharacterData::getStatusEffectEnd() const +{ + return mEntity->getComponent<BeingComponent>()->getStatusEffects().end(); +} + +inline int CharacterData::getKillCountSize() const +{ + return mCharacterComponent->getKillCountSize(); +} + +inline const std::map<int, int>::const_iterator CharacterData::getKillCountBegin() const +{ + return mCharacterComponent->getKillCountBegin(); +} + +inline const std::map<int, int>::const_iterator CharacterData::getKillCountEnd() const +{ + return mCharacterComponent->getKillCountEnd(); +} + +inline void CharacterData::setKillCount(int monsterId, int kills) +{ + mCharacterComponent->setKillCount(monsterId, kills); +} + +inline void CharacterData::clearSpecials() +{ + mCharacterComponent->clearSpecials(); +} + +inline void CharacterData::giveSpecial(int id, int mana) +{ + mCharacterComponent->giveSpecial(id, mana); +} + +inline int CharacterData::getSpecialSize() const +{ + return mCharacterComponent->getSpecialSize(); +} + +inline SpecialMap::const_iterator CharacterData::getSpecialBegin() const +{ + return mCharacterComponent->getSpecialBegin(); +} + +inline SpecialMap::const_iterator CharacterData::getSpecialEnd() const +{ + return mCharacterComponent->getSpecialEnd(); +} + +inline Possessions &CharacterData::getPossessions() const +{ + return mCharacterComponent->getPossessions(); +} + #endif // CHARACTER_H diff --git a/src/game-server/collisiondetection.cpp b/src/game-server/collisiondetection.cpp index bf8c7ce3..562f4407 100644 --- a/src/game-server/collisiondetection.cpp +++ b/src/game-server/collisiondetection.cpp @@ -27,6 +27,9 @@ #define D_TO_R 0.0174532925 // PI / 180 #define R_TO_D 57.2957795 // 180 / PI +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif // Tests to see if pos is between s1 degree and s2 #define test_degrees(pos,s1,s2) (pos > s1 && pos < s2) || (s1 > s2 && !(pos < s1 && pos > s2)) diff --git a/src/game-server/combatcomponent.cpp b/src/game-server/combatcomponent.cpp new file mode 100644 index 00000000..38c7716e --- /dev/null +++ b/src/game-server/combatcomponent.cpp @@ -0,0 +1,227 @@ +/* + * The Mana Server + * Copyright (C) 2013 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 "combatcomponent.h" + +#include "game-server/being.h" +#include "game-server/mapcomposite.h" + +#include "utils/logger.h" + +CombatComponent::CombatComponent(Entity &being): + mTarget(nullptr), + mCurrentAttack(nullptr) +{ + being.getComponent<BeingComponent>()->signal_died.connect(sigc::mem_fun( + this, &CombatComponent::diedOrRemoved)); + being.signal_removed.connect(sigc::mem_fun(this, + &CombatComponent::diedOrRemoved)); +} + +CombatComponent::~CombatComponent() +{ +} + +void CombatComponent::update(Entity &entity) +{ + auto *beingComponent = entity.getComponent<BeingComponent>(); + + if (beingComponent->getAction() != ATTACK || !mTarget) + return; + + std::vector<Attack *> attacksReady; + mAttacks.getUsuableAttacks(&attacksReady); + + if (Attack *triggerableAttack = mAttacks.getTriggerableAttack()) + { + processAttack(entity, *triggerableAttack); + mAttacks.markAttackAsTriggered(); + } + + // Deal with the ATTACK action. + if (attacksReady.empty()) + return; + + Attack *highestPriorityAttack = 0; + // Performs all ready attacks. + for (std::vector<Attack *>::const_iterator it = attacksReady.begin(), + it_end = attacksReady.end(); it != it_end; ++it) + { + const Point &attackerPosition = + entity.getComponent<ActorComponent>()->getPosition(); + const Point &targetPosition = + mTarget->getComponent<ActorComponent>()->getPosition(); + + // check if target is in range using the pythagorean theorem + int distx = attackerPosition.x - targetPosition.x; + int disty = attackerPosition.y - targetPosition.y; + int distSquare = (distx * distx + disty * disty); + AttackInfo *info = (*it)->getAttackInfo(); + int maxDist = info->getDamage().range + + entity.getComponent<ActorComponent>()->getSize(); + + if (distSquare <= maxDist * maxDist && + (!highestPriorityAttack || + highestPriorityAttack->getAttackInfo()->getPriority() + < info->getPriority())) + { + highestPriorityAttack = *it; + } + } + if (highestPriorityAttack) + { + mAttacks.startAttack(highestPriorityAttack); + mCurrentAttack = highestPriorityAttack; + const Point &point = + entity.getComponent<ActorComponent>()->getPosition(); + beingComponent->setDestination(entity, point); + // TODO: Turn into direction of enemy + entity.getComponent<ActorComponent>()->raiseUpdateFlags( + UPDATEFLAG_ATTACK); + } +} + +/** + * Takes a damage structure, computes the real damage based on the + * stats, deducts the result from the hitpoints and adds the result to + * the HitsTaken list. + */ +int CombatComponent::damage(Entity &target, + Entity *source, + const Damage &damage) +{ + auto *beingComponent = target.getComponent<BeingComponent>(); + + int HPloss = damage.base; + if (damage.delta) + HPloss += rand() * (damage.delta + 1) / RAND_MAX; + + // TODO magical attacks and associated elemental modifiers + switch (damage.type) + { + case DAMAGE_PHYSICAL: + { + const double dodge = + beingComponent->getModifiedAttribute(ATTR_DODGE); + if (!damage.trueStrike && rand()%((int)dodge + 1) > + rand()%(damage.cth + 1)) + { + HPloss = 0; + // TODO Process triggers for a dodged physical attack here. + // If there is an attacker included, also process triggers for the attacker (failed physical strike) + } + else + { + const double defense = + beingComponent->getModifiedAttribute(ATTR_DEFENSE); + HPloss = HPloss * (1.0 - (0.0159375f * defense) / + (1.0 + 0.017 * defense)) + + (rand()%((HPloss / 16) + 1)); + // TODO Process triggers for receiving damage here. + // If there is an attacker included, also process triggers for the attacker (successful physical strike) + } + break; + } + case DAMAGE_MAGICAL: +#if 0 + beingComponent.getModifiedAttribute(BASE_ELEM_BEGIN + damage.element); +#else + LOG_WARN("Attempt to use magical type damage! This has not been" + "implemented yet and should not be used!"); + HPloss = 0; +#endif + break; + case DAMAGE_DIRECT: + break; + default: + LOG_WARN("Unknown damage type '" << damage.type << "'!"); + break; + } + + if (HPloss > 0) + { + mHitsTaken.push_back(HPloss); + const Attribute *HP = beingComponent->getAttribute(ATTR_HP); + LOG_DEBUG("Being " + << target.getComponent<ActorComponent>()->getPublicID() + << " suffered " << HPloss + << " damage. HP: " + << HP->getModifiedAttribute() << "/" + << beingComponent->getModifiedAttribute(ATTR_MAX_HP)); + beingComponent->setAttribute(target, ATTR_HP, HP->getBase() - HPloss); + // No HP regen after being hit if this is set. + // TODO: Reenable this once the attributes are available as a component + // A bit too fuzzy to implement at the moment + //mHealthRegenerationTimeout.setSoft( + // Configuration::getValue("game_hpRegenBreakAfterHit", 0)); + } + else + { + HPloss = 0; + } + + signal_damaged.emit(source, damage, HPloss); + return HPloss; +} + +/** + * Performs an attack + */ +void CombatComponent::processAttack(Entity &source, Attack &attack) +{ + performAttack(source, attack.getAttackInfo()->getDamage()); +} + +/** + * Adds an attack to the available attacks + */ +void CombatComponent::addAttack(AttackInfo *attackInfo) +{ + mAttacks.add(this, attackInfo); +} + +/** + * Removes an attack from the available attacks + */ +void CombatComponent::removeAttack(AttackInfo *attackInfo) +{ + mAttacks.remove(this, attackInfo); +} + +/** + * Performs an attack. + */ +int CombatComponent::performAttack(Entity &source, const Damage &dmg) +{ + // check target legality + if (!mTarget + || mTarget == &source + || mTarget->getComponent<BeingComponent>()->getAction() == DEAD + || !mTarget->canFight()) + return -1; + + if (source.getMap()->getPvP() == PVP_NONE + && mTarget->getType() == OBJECT_CHARACTER + && source.getType() == OBJECT_CHARACTER) + return -1; + + return mTarget->getComponent<CombatComponent>()->damage(*mTarget, + &source, dmg); +} diff --git a/src/game-server/combatcomponent.h b/src/game-server/combatcomponent.h new file mode 100644 index 00000000..168c08d7 --- /dev/null +++ b/src/game-server/combatcomponent.h @@ -0,0 +1,143 @@ +/* + * The Mana Server + * Copyright (C) 2013 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/>. + */ + +#ifndef COMBATCOMPONENT_H +#define COMBATCOMPONENT_H + +#include "component.h" + +#include <vector> + +#include <sigc++/trackable.h> + +#include "game-server/attack.h" + +class Entity; + +/** + * Type definition for a list of hits + */ +typedef std::vector<unsigned> Hits; + +class CombatComponent: public Component +{ +public: + static const ComponentType type = CT_Fighting; + + CombatComponent(Entity &being); + virtual ~CombatComponent(); + + void update(Entity &entity); + + void addAttack(AttackInfo *attack); + void removeAttack(AttackInfo *attackInfo); + Attacks &getAttacks(); + + const Hits &getHitsTaken() const; + void clearHitsTaken(); + + int performAttack(Entity &source, const Damage &dmg); + virtual int damage(Entity &target, Entity *source, const Damage &damage); + + int getAttackId() const; + + Entity *getTarget() const; + void setTarget(Entity *target); + void clearTarget(); + + void diedOrRemoved(Entity *entity); + + sigc::signal<void, Entity *, const Damage &, int> signal_damaged; + +protected: + virtual void processAttack(Entity &source, Attack &attack); + + Entity *mTarget; + Attacks mAttacks; + Attack *mCurrentAttack; // Last used attack + Hits mHitsTaken; //List of punches taken since last update. + +}; + +inline Attacks &CombatComponent::getAttacks() +{ + return mAttacks; +} + +/** + * Gets the damage list. + */ +inline const Hits &CombatComponent::getHitsTaken() const +{ + return mHitsTaken; +} + +/** + * Clears the damage list. + */ +inline void CombatComponent::clearHitsTaken() +{ + mHitsTaken.clear(); +} + +/** + * Gets the attack id the being is currently performing. + * For being, this is defaulted to the first one (1). + */ +inline int CombatComponent::getAttackId() const +{ + return mCurrentAttack ? + mCurrentAttack->getAttackInfo()->getDamage().id : 0; +} + +/** + * Get Target + */ +inline Entity *CombatComponent::getTarget() const +{ + return mTarget; +} + +/** + * Set Target + */ +inline void CombatComponent::setTarget(Entity *target) +{ + mTarget = target; +} + +/** + * Clears the target + */ +inline void CombatComponent::clearTarget() +{ + mTarget = nullptr; +} + +/** + * Handler for the died and removed event of the targeting being + * @param entity The removed/died being (not used here) + */ +inline void CombatComponent::diedOrRemoved(Entity *entity) +{ + clearTarget(); +} + +#endif // COMBATCOMPONENT_H diff --git a/src/game-server/command.cpp b/src/game-server/command.cpp deleted file mode 100644 index e80b9f98..00000000 --- a/src/game-server/command.cpp +++ /dev/null @@ -1,438 +0,0 @@ -/* - * The Mana Server - * Copyright (C) 2007-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/>. - */ - -#include <cstddef> - -#include "game-server/accountconnection.h" -#include "game-server/character.h" -#include "game-server/gamehandler.h" -#include "game-server/inventory.h" -#include "game-server/item.h" -#include "game-server/itemmanager.h" -#include "game-server/mapmanager.h" -#include "game-server/monster.h" -#include "game-server/monstermanager.h" -#include "game-server/state.h" - -template< typename T > -static T proxy_cast(intptr_t v) -{ return (T)v; } - -template<> -const std::string &proxy_cast(intptr_t v) -{ return *(const std::string *)v; } - -template< typename T1 > -static void proxy(void (*f)(), Character *from, intptr_t const args[1]) -{ - ((void (*)(Character *, T1))f) - (from, proxy_cast<T1>(args[0])); -} - -template< typename T1, typename T2 > -static void proxy(void (*f)(), Character *from, intptr_t const args[2]) -{ - ((void (*)(Character *, T1, T2))f) - (from, proxy_cast<T1>(args[0]), proxy_cast<T2>(args[1])); -} - -template< typename T1, typename T2, typename T3 > -static void proxy(void (*f)(), Character *from, intptr_t const args[3]) -{ - ((void (*)(Character *, T1, T2, T3))f) - (from, proxy_cast<T1>(args[0]), proxy_cast<T2>(args[1]), - proxy_cast<T3>(args[2])); -} - -template< typename T1, typename T2, typename T3, typename T4 > -static void proxy(void (*f)(), Character *from, intptr_t const args[4]) -{ - ((void (*)(Character *, T1, T2, T3, T4))f) - (from, proxy_cast<T1>(args[0]), proxy_cast<T2>(args[1]), - proxy_cast<T3>(args[2]), proxy_cast<T4>(args[3])); -} - -/** - * An argument type that a command can use. - */ - -template< typename T > struct Argument; - -template<> struct Argument< int > -{ static char const type = 'n'; }; -template<> struct Argument< const std::string & > -{ static char const type = 's'; }; -template<> struct Argument< Character * > -{ static char const type = 'c'; }; -template<> struct Argument< MapComposite * > -{ static char const type = 'm'; }; -template<> struct Argument< ItemClass * > -{ static char const type = 'i'; }; -template<> struct Argument< MonsterClass * > -{ static char const type = 'o'; }; - -/** - * A command that a user can run remotely with sufficient rights. - */ -struct Command -{ - const char *name; - void (*handler)(void (*f)(), Character *, intptr_t const[]); - void (*target)(); - char type[4]; - unsigned char level; -}; - -/** - * Creates a command with a 1-parameter handler. - */ -template< typename T1 > -static Command handle(const char *name, int level, - void (*f)(Character *, T1)) -{ - Command c; - c.name = name; - c.level = level; - c.handler = &proxy< T1 >; - c.target = (void (*)())f; - c.type[0] = Argument<T1>::type; - c.type[1] = 0; - return c; -} - -/** - * Creates a command with a 2-parameter handler. - */ -template< typename T1, typename T2 > -static Command handle(const char *name, int level, - void (*f)(Character *, T1, T2)) -{ - Command c; - c.name = name; - c.level = level; - c.handler = &proxy< T1, T2 >; - c.target = (void (*)())f; - c.type[0] = Argument<T1>::type; - c.type[1] = Argument<T2>::type; - c.type[2] = 0; - return c; -} - -/** - * Creates a command with a 3-parameter handler. - */ -template< typename T1, typename T2, typename T3 > -static Command handle(const char *name, int level, - void (*f)(Character *, T1, T2, T3)) -{ - Command c; - c.name = name; - c.level = level; - c.handler = &proxy< T1, T2, T3 >; - c.target = (void (*)())f; - c.type[0] = Argument<T1>::type; - c.type[1] = Argument<T2>::type; - c.type[2] = Argument<T3>::type; - c.type[3] = 0; - return c; -} - -/** - * Creates a command with a 4-parameter handler. - */ -template< typename T1, typename T2, typename T3, typename T4 > -static Command handle(const char *name, int level, - void (*f)(Character *, T1, T2, T3, T4)) -{ - Command c; - c.name = name; - c.level = level; - c.handler = &proxy< T1, T2, T3, T4 >; - c.target = (void (*)())f; - c.type[0] = Argument<T1>::type; - c.type[1] = Argument<T2>::type; - c.type[2] = Argument<T3>::type; - c.type[3] = Argument<T4>::type; - return c; -} - -static void warp(Character *, Character *q, MapComposite *m, int x, int y) -{ - GameState::warp(q, m, x, y); -} - -static void item(Character *, Character *q, ItemClass *it, int nb) -{ - Inventory(q).insert(it->getDatabaseID(), nb); -} - -// This no longer works as money is now an attribute. -/* -static void money(Character *, Character *q, int nb) -{ - Inventory(q).changeMoney(nb); -} -*/ - -static void drop(Character *from, ItemClass *it, int nb) -{ - Item *item = new Item(it, nb); - item->setMap(from->getMap()); - item->setPosition(from->getPosition()); - GameState::insertOrDelete(item); -} - -static void spawn(Character *from, MonsterClass *specy, int nb) -{ - MapComposite *map = from->getMap(); - const Point &pos = from->getPosition(); - - for (int i = 0; i < nb; ++i) - { - Being *monster = new Monster(specy); - monster->setMap(map); - monster->setPosition(pos); - monster->clearDestination(); - if (!GameState::insertOrDelete(monster)) - { - // The map is full. Break out. - break; - } - } -} - -static void goto_(Character *from, Character *ch) -{ - MapComposite *m = ch->getMap(); - const Point &pos = ch->getPosition(); - GameState::warp(from, m, pos.x, pos.y); -} - -static void recall(Character *from, Character *ch) -{ - MapComposite *m = from->getMap(); - const Point &pos = from->getPosition(); - GameState::warp(ch, m, pos.x, pos.y); -} - -static void reload(Character *, const std::string &db) -{ - if (db == "items") - { - itemManager->reload(); - } - else if (db == "monsters") - { - monsterManager->reload(); - } -} - -static void ban(Character *from, Character *ch, const std::string &duration) -{ - if (from->getAccountLevel() <= ch->getAccountLevel()) - { - // Special case: Only ban strictly less priviledged accounts. - return; - } - - int d = atoi(duration.c_str()); - switch (duration[duration.length() - 1]) - { - case 'd': d = d * 24; // nobreak - case 'h': d = d * 60; break; - } - accountHandler->banCharacter(ch, d); -} - -static void attribute(Character*, Character *ch, int attr, int value) -{ - ch->setAttribute(attr, value); -} - -/** - * List of remote commands. - */ -static Command const commands[] = -{ - handle("warp", AL_GM, warp), - handle("item", AL_GM, item), - handle("drop", AL_GM, drop), -// handle("money", AL_GM, money), - handle("spawn", AL_GM, spawn), - handle("goto", AL_GM, goto_), - handle("recall", AL_GM, recall), - handle("reload", AL_ADMIN, reload), - handle("ban", AL_GM, ban), - handle("attribute", AL_GM, attribute), -}; - -/** - * Send a message to the given character from the server. - */ -static void say(Character * ch, const std::string &message) -{ - GameState::sayTo(ch, NULL, message); -} - - -/** - * Parses a command and executes its associated handler. - */ -void runCommand(Character *ch, const std::string &text) -{ - const Command *c = NULL; - std::string::size_type npos = std::string::npos; - std::string::size_type pos = text.find(' '); - // remove the first letter which signifies it was a command - std::string s(text, 1, pos == npos ? npos : pos - 1); - - // check for valid command - for (int i = 0; i < (int)(sizeof(commands) / sizeof(commands[0])); ++i) - { - if (s == commands[i].name) - { - c = &commands[i]; - break; - } - } - - if (!c) - { - say(ch, "The command " + s + " was not found"); - return; - } - - if (c->level > ch->getAccountLevel()) - { - say(ch, "You have insufficient rights to perform the " + s + " command"); - return; - } - - intptr_t args[4]; - - for (int i = 0; i < 4 && c->type[i]; ++i) - { - if (pos == npos || pos + 1 >= text.length()) - { - // Not enough parameters. - say(ch, "Not enough parameters for the " + s + - " command. See the command documentation for details"); - return; - } - - std::string::size_type pos2 = text.find(' ', pos + 1); - std::string arg(text, pos + 1, pos2 == npos ? npos : pos2 - pos - 1); - if (arg.empty()) - { - // Empty parameter. - say(ch, "One of your parameters was empty"); - return; - } - - switch (c->type[i]) - { - case 'c': - if (arg == "#") - { - // Character itself. - args[i] = (intptr_t)ch; - } - else - { - Character *c = gameHandler->getCharacterByNameSlow(arg); - if (!c) - { - /* TODO: forward command to other game servers through - account server, in case the player is elsewhere. */ - say(ch, "Player " + arg + " was not found"); - return; - } - args[i] = (intptr_t)c; - } - break; - - case 'i': - if (ItemClass *ic = itemManager->getItem(atoi(arg.c_str()))) - { - args[i] = (intptr_t)ic; - } - else - { - // No such item. - say(ch, "No item was found with id " + arg); - return; - } - break; - - case 'm': - if (arg == "#") - { - // Map the character is on. - args[i] = (intptr_t)ch->getMap(); - } - else if (MapComposite *m = MapManager::getMap(atoi(arg.c_str()))) - { - args[i] = (intptr_t)m; - } - else - { - // No such map. - say(ch, "Map " + arg + " was not found"); - return; - } - break; - - case 'n': - args[i] = atoi(arg.c_str()); - break; - - case 'o': - if (MonsterClass *mc = monsterManager->getMonster(atoi(arg.c_str()))) - { - args[i] = (intptr_t)mc; - } - else - { - // No such item. - say(ch, "No monster with id " + arg + " was found"); - return; - } - break; - - case 's': - args[i] = (intptr_t)new std::string(arg); - break; - - } - pos = pos2; - } - - // Call the command handler. - c->handler(c->target, ch, args); - - // Delete dynamic arguments. - for (int i = 0; i < 4 && c->type[i]; ++i) - { - if (c->type[i] == 's') - { - delete (std::string *)args[i]; - } - } -} diff --git a/src/game-server/commandhandler.cpp b/src/game-server/commandhandler.cpp index 6de22242..9c413ed8 100644 --- a/src/game-server/commandhandler.cpp +++ b/src/game-server/commandhandler.cpp @@ -48,44 +48,44 @@ struct CmdRef const char *cmd; const char *usage; const char *help; - void (*func)(Character*, std::string&) ; + void (*func)(Entity*, std::string&) ; }; -static void handleHelp(Character*, std::string&); -static void handleReport(Character*, std::string&); -static void handleWhere(Character*, std::string&); -static void handleRights(Character*, std::string&); -static void handleWarp(Character*, std::string&); -static void handleCharWarp(Character*, std::string&); -static void handleGoto(Character*, std::string&); -static void handleRecall(Character*, std::string&); -static void handleBan(Character*, std::string&); -static void handleItem(Character*, std::string&); -static void handleDrop(Character*, std::string&); -static void handleMoney(Character*, std::string&); -static void handleSpawn(Character*, std::string&); -static void handleAttribute(Character*, std::string&); -static void handleReload(Character*, std::string&); -static void handlePermissions(Character*, std::string&); -static void handleGivePermission(Character*, std::string&); -static void handleTakePermission(Character*, std::string&); -static void handleAnnounce(Character*, std::string&); -static void handleHistory(Character*, std::string&); -static void handleMute(Character*, std::string&); -static void handleDie(Character*, std::string&); -static void handleKill(Character*, std::string&); -static void handleKick(Character*, std::string&); -static void handleLog(Character*, std::string&); -static void handleLogsay(Character*, std::string&); -static void handleKillMonsters(Character*, std::string&); -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 void handleHelp(Entity*, std::string&); +static void handleReport(Entity*, std::string&); +static void handleWhere(Entity*, std::string&); +static void handleRights(Entity*, std::string&); +static void handleWarp(Entity*, std::string&); +static void handleCharWarp(Entity*, std::string&); +static void handleGoto(Entity*, std::string&); +static void handleRecall(Entity*, std::string&); +static void handleBan(Entity*, std::string&); +static void handleItem(Entity*, std::string&); +static void handleDrop(Entity*, std::string&); +static void handleMoney(Entity*, std::string&); +static void handleSpawn(Entity*, std::string&); +static void handleAttribute(Entity*, std::string&); +static void handleReload(Entity*, std::string&); +static void handlePermissions(Entity*, std::string&); +static void handleGivePermission(Entity*, std::string&); +static void handleTakePermission(Entity*, std::string&); +static void handleAnnounce(Entity*, std::string&); +static void handleHistory(Entity*, std::string&); +static void handleMute(Entity*, std::string&); +static void handleDie(Entity*, std::string&); +static void handleKill(Entity*, std::string&); +static void handleKick(Entity*, std::string&); +static void handleLog(Entity*, std::string&); +static void handleLogsay(Entity*, std::string&); +static void handleKillMonsters(Entity*, std::string&); +static void handleCraft(Entity*, std::string&); +static void handleGetPos(Entity*, std::string&); +static void handleSkills(Entity*, std::string&); +static void handleEffect(Entity*, std::string&); +static void handleGiveSpecial(Entity*, std::string&); +static void handleTakeSpecial(Entity*, std::string&); +static void handleRechargeSpecial(Entity*, std::string&); +static void handleListSpecials(Entity*, std::string&); static CmdRef const cmdRef[] = { @@ -171,9 +171,9 @@ static CmdRef const cmdRef[] = }; -static void say(const std::string message, Character *player) +static void say(const std::string &message, Entity *player) { - GameState::sayTo(player, NULL, message); + GameState::sayTo(player, nullptr, message); } /* @@ -193,10 +193,10 @@ static bool checkPermission(Character *player, unsigned permissions) * Returns the next argument, and remove it from the given string. */ -static std::string playerRights(Character *ch) +static std::string playerRights(Entity *ch) { std::stringstream str; - str << (unsigned)ch->getAccountLevel(); + str << (unsigned)ch->getComponent<CharacterComponent>()->getAccountLevel(); str << " ( "; std::list<std::string> classes = PermissionManager::getClassList(ch); @@ -263,7 +263,7 @@ static std::string getArgument(std::string &args) return argument; } -static void handleHelp(Character *player, std::string &args) +static void handleHelp(Entity *player, std::string &args) { if (args.empty()) { @@ -302,7 +302,7 @@ static void handleHelp(Character *player, std::string &args) } } -static void handleWarp(Character *player, std::string &args) +static void handleWarp(Entity *player, std::string &args) { int x, y; MapComposite *map; @@ -377,20 +377,22 @@ static void handleWarp(Character *player, std::string &args) y = utils::stringToInt(ystr); // now warp the player - GameState::warp(player, map, x, y); + GameState::warp(player, map, Point(x, y)); // log transaction std::stringstream ss; ss << "User warped to " << map->getName() << " (" << x << ", " << y << ")"; - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_WARP, + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_WARP, ss.str()); } -static void handleCharWarp(Character *player, std::string &args) +static void handleCharWarp(Entity *player, std::string &args) { int x, y; MapComposite *map; - Character *other; + Entity *other; // get the arguments std::string character = getArgument(args); @@ -479,19 +481,20 @@ static void handleCharWarp(Character *player, std::string &args) y = utils::stringToInt(ystr); // now warp the player - GameState::warp(other, map, x, y); + GameState::warp(other, map, Point(x, y)); // log transaction std::stringstream ss; - ss << "User warped " << other->getName() << " to " << map->getName() << - " (" << x << ", " << y << ")"; - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_WARP, - ss.str()); + ss << "User warped " << other->getComponent<BeingComponent>()->getName() + << " to " << map->getName() << " (" << x << ", " << y << ")"; + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_WARP, ss.str()); } -static void handleItem(Character *player, std::string &args) +static void handleItem(Entity *player, std::string &args) { - Character *other; + Entity *other; ItemClass *ic; int value = 0; @@ -562,17 +565,19 @@ static void handleItem(Character *player, std::string &args) // log transaction std::stringstream str; str << "User created item " << ic->getDatabaseID(); - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_ITEM, str.str()); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_ITEM, str.str()); } -static void handleDrop(Character *player, std::string &args) +static void handleDrop(Entity *player, std::string &args) { ItemClass *ic; - int value = 0; + int amount = 0; // get arguments std::string itemclass = getArgument(args); - std::string valuestr = getArgument(args); + std::string amountstr = getArgument(args); // check all arguments are there if (itemclass.empty()) @@ -598,37 +603,39 @@ static void handleDrop(Character *player, std::string &args) return; } - //identify the amount - if (valuestr.empty()) + // identify the amount + if (amountstr.empty()) { - value = 1; + amount = 1; } - else if (utils::isNumeric(valuestr)) + else if (utils::isNumeric(amountstr)) { - value = utils::stringToInt(valuestr); + amount = utils::stringToInt(amountstr); } // check for valid amount - if (value <= 0) + if (amount <= 0) { say("Invalid number of items", player); return; } - // create the integer and put it on the map - Item *item = new Item(ic, value); - item->setMap(player->getMap()); - item->setPosition(player->getPosition()); + const Point &position = + player->getComponent<ActorComponent>()->getPosition(); + Entity *item = Item::create(player->getMap(), position, ic, amount); + GameState::insertOrDelete(item); // log transaction std::stringstream str; str << "User created item " << ic->getDatabaseID(); - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_DROP, str.str()); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_DROP, str.str()); } -static void handleMoney(Character *player, std::string &args) +static void handleMoney(Entity *player, std::string &args) { - Character *other; + Entity *other; int value; // get arguments @@ -669,19 +676,24 @@ static void handleMoney(Character *player, std::string &args) // change value into an integer value = utils::stringToInt(valuestr); + auto *beingComponent = other->getComponent<BeingComponent>(); + // change how much money the player has - other->setAttribute(ATTR_GP , other->getAttribute(ATTR_GP) + value); + const double previousMoney = beingComponent->getAttributeBase(ATTR_GP); + beingComponent->setAttribute(*player, ATTR_GP , previousMoney + value); // log transaction std::string msg = "User created " + valuestr + " money"; - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_MONEY, msg); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_MONEY, msg); } -static void handleSpawn(Character *player, std::string &args) +static void handleSpawn(Entity *player, std::string &args) { MonsterClass *mc; MapComposite *map = player->getMap(); - const Point &pos = player->getPosition(); + const Point &pos = player->getComponent<ActorComponent>()->getPosition(); int value = 0; // get the arguments @@ -732,10 +744,13 @@ static void handleSpawn(Character *player, std::string &args) // create the monsters and put them on the map for (int i = 0; i < value; ++i) { - Being *monster = new Monster(mc); + Entity *monster = new Entity(OBJECT_MONSTER); + auto *actorComponent = new ActorComponent(*monster); + monster->addComponent(actorComponent); + actorComponent->setPosition(*monster, pos); + monster->addComponent(new BeingComponent(*monster)); + monster->addComponent(new MonsterComponent(*monster, mc)); monster->setMap(map); - monster->setPosition(pos); - monster->clearDestination(); if (!GameState::insertOrDelete(monster)) { // The map is full. Break out. @@ -743,14 +758,17 @@ static void handleSpawn(Character *player, std::string &args) } // log transaction - std::string msg = "User created monster " + monster->getName(); - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_SPAWN, msg); + std::string msg = "User created monster " + + monster->getComponent<BeingComponent>()->getName(); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_SPAWN, msg); } } -static void handleGoto(Character *player, std::string &args) +static void handleGoto(Entity *player, std::string &args) { - Character *other; + Entity *other; // get the arguments std::string character = getArgument(args); @@ -773,18 +791,21 @@ static void handleGoto(Character *player, std::string &args) // move the player to where the other player is MapComposite *map = other->getMap(); - const Point &pos = other->getPosition(); - GameState::warp(player, map, pos.x, pos.y); + const Point &pos = other->getComponent<ActorComponent>()->getPosition(); + GameState::warp(player, map, pos); // log transaction std::stringstream msg; - msg << "User warped own character to " << other->getName(); - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_GOTO, msg.str()); + msg << "User warped own character to " + << other->getComponent<BeingComponent>()->getName(); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_GOTO, msg.str()); } -static void handleRecall(Character *player, std::string &args) +static void handleRecall(Entity *player, std::string &args) { - Character *other; + Entity *other; // get the arguments std::string character = getArgument(args); @@ -807,20 +828,20 @@ static void handleRecall(Character *player, std::string &args) // move the other player to where the player is MapComposite *map = player->getMap(); - const Point &pos = player->getPosition(); - GameState::warp(other, map, pos.x, pos.y); + const Point &pos = player->getComponent<ActorComponent>()->getPosition(); + GameState::warp(other, map, pos); } -static void handleReload(Character *, std::string &) +static void handleReload(Entity *, std::string &) { // reload the items and monsters itemManager->reload(); monsterManager->reload(); } -static void handleBan(Character *player, std::string &args) +static void handleBan(Entity *player, std::string &args) { - Character *other; + Entity *other; int length; int lengthMutiplier = 0; @@ -876,22 +897,26 @@ static void handleBan(Character *player, std::string &args) return; } + auto *characterComponent = player->getComponent<CharacterComponent>(); + // ban the player accountHandler->banCharacter(other, length); // disconnect the player MessageOut kickmsg(GPMSG_CONNECT_RESPONSE); kickmsg.writeInt8(ERRMSG_ADMINISTRATIVE_LOGOFF); - other->getClient()->disconnect(kickmsg); + characterComponent->getClient()->disconnect(kickmsg); // feedback for command user - std::string msg = "You've banned " + other->getName() + " for " + utils::toString(length) + " minutes"; + std::string otherName = other->getComponent<BeingComponent>()->getName(); + std::string msg = "You've banned " + otherName + " for " + utils::toString(length) + " minutes"; say(msg.c_str(), player); // log transaction - msg = "User banned " + other->getName() + " for " + utils::toString(length) + " minutes"; - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_BAN, msg); + msg = "User banned " + otherName + " for " + utils::toString(length) + " minutes"; + accountHandler->sendTransaction(characterComponent->getDatabaseID(), + TRANS_CMD_BAN, msg); } -static void handlePermissions(Character *player, std::string &args) +static void handlePermissions(Entity *player, std::string &args) { std::string character = getArgument(args); if (character.empty()) @@ -901,20 +926,20 @@ static void handlePermissions(Character *player, std::string &args) return; } - Character *other = gameHandler->getCharacterByNameSlow(character); + Entity *other = gameHandler->getCharacterByNameSlow(character); if (!other) { say("Invalid character", player); return; } - say(other->getName() + " has the permissions: " + + say(other->getComponent<BeingComponent>()->getName() + " has the permissions: " + playerRights(other), player); } -static void handleGivePermission(Character *player, std::string &args) +static void handleGivePermission(Entity *player, std::string &args) { - Character *other; + Entity *other; // get the arguments std::string character = getArgument(args); @@ -952,30 +977,37 @@ static void handleGivePermission(Character *player, std::string &args) return; } - if (permission & other->getAccountLevel()) + auto *characterComponent = + player->getComponent<CharacterComponent>(); + + if (permission & characterComponent->getAccountLevel()) { - say(player->getName()+" already has the permission "+strPermission, player); + say(player->getComponent<BeingComponent>()->getName() + +" already has the permission "+strPermission, player); } else { - permission += other->getAccountLevel(); + permission += characterComponent->getAccountLevel(); // change the player's account level - other->setAccountLevel(permission); + characterComponent->setAccountLevel(permission); accountHandler->changeAccountLevel(other, permission); // log transaction - std::string msg = "User gave right " + strPermission + " to " + other->getName(); - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_SETGROUP, msg); - say("You gave " + other->getName() + + std::string msg = "User gave right " + strPermission + " to " + + other->getComponent<BeingComponent>()->getName(); + accountHandler->sendTransaction(characterComponent->getDatabaseID(), + TRANS_CMD_SETGROUP, msg); + say("You gave " + other->getComponent<BeingComponent>()->getName() + " the rights of a " + strPermission, player); - say("Congratulations, " + player->getName() + + say("Congratulations, " + + player->getComponent<BeingComponent>()->getName() + " gave you the rights of a " + strPermission, other); } } -static void handleTakePermission(Character *player, std::string &args) +static void handleTakePermission(Entity *player, std::string &args) { - Character *other; + Entity *other; // get the arguments std::string character = getArgument(args); @@ -1013,27 +1045,33 @@ static void handleTakePermission(Character *player, std::string &args) return; } + auto *characterComponent = + player->getComponent<CharacterComponent>(); - if (!(permission & other->getAccountLevel())) + if (!(permission & characterComponent->getAccountLevel())) { - say(player->getName()+" hasn't got the permission "+strPermission, player); + say(player->getComponent<BeingComponent>()->getName() + +" hasn't got the permission "+strPermission, player); } else { - permission = other->getAccountLevel() - permission; + permission = characterComponent->getAccountLevel() - permission; // change the player's account level - other->setAccountLevel(permission); + characterComponent->setAccountLevel(permission); accountHandler->changeAccountLevel(other, permission); // log transaction - std::string msg = "User took right " + strPermission + " from " + other->getName(); - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_SETGROUP, msg); - say("Sorry, "+player->getName()+" revoked your rights of a "+strPermission, other); + std::string msg = "User took right " + strPermission + " from " + + other->getComponent<BeingComponent>()->getName(); + accountHandler->sendTransaction(characterComponent->getDatabaseID(), + TRANS_CMD_SETGROUP, msg); + say("Sorry, "+player->getComponent<BeingComponent>()->getName() + +" revoked your rights of a "+strPermission, other); } } -static void handleAttribute(Character *player, std::string &args) +static void handleAttribute(Entity *player, std::string &args) { - Character *other; + Entity *other; int attr, value; // get arguments @@ -1090,17 +1128,22 @@ static void handleAttribute(Character *player, std::string &args) return; } + auto *beingComponent = other->getComponent<BeingComponent>(); + // change the player's attribute - other->setAttribute(attr, value); + beingComponent->setAttribute(*other, attr, value); // log transaction std::stringstream msg; - msg << "User changed attribute " << attr << " of player " << other->getName() + msg << "User changed attribute " << attr << " of player " + << beingComponent->getName() << " to " << value; - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_ATTRIBUTE, msg.str()); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_ATTRIBUTE, msg.str()); } -static void handleReport(Character *player, std::string &args) +static void handleReport(Entity *player, std::string &args) { std::string bugReport = getArgument(args); @@ -1114,7 +1157,7 @@ static void handleReport(Character *player, std::string &args) // TODO: Send the report to a developer or something } -static void handleAnnounce(Character *player, std::string &args) +static void handleAnnounce(Entity *player, std::string &args) { if (args.empty()) { @@ -1125,37 +1168,40 @@ static void handleAnnounce(Character *player, std::string &args) MessageOut msg(GAMSG_ANNOUNCE); msg.writeString(args); - msg.writeInt16(player->getDatabaseID()); - msg.writeString(player->getName()); + msg.writeInt16(player->getComponent<CharacterComponent>() + ->getDatabaseID()); + msg.writeString(player->getComponent<BeingComponent>()->getName()); accountHandler->send(msg); } -static void handleWhere(Character *player, std::string &) +static void handleWhere(Entity *player, std::string &) { + const Point &position = + player->getComponent<ActorComponent>()->getPosition(); std::stringstream str; str << "Your current location is map " - << player->getMapId() + << player->getMap()->getID() << " [" - << player->getPosition().x + << position.x << ":" - << player->getPosition().y + << position.y << "]"; say (str.str(), player); } -static void handleRights(Character *player, std::string &) +static void handleRights(Entity *player, std::string &) { say("Your rights level is: " + playerRights(player), player); } -static void handleHistory(Character *, std::string &) +static void handleHistory(Entity *, std::string &) { // TODO: Get args number of transactions and show them to the player } -static void handleMute(Character *player, std::string &args) +static void handleMute(Entity *player, std::string &args) { - Character *other; + Entity *other; int length; // Get arguments. @@ -1184,23 +1230,28 @@ static void handleMute(Character *player, std::string &args) } // Mute the player. - other->mute(length); + other->getComponent<CharacterComponent>()->mute(length); + + const std::string &playerName = + player->getComponent<BeingComponent>()->getName(); + const std::string &otherName = + other->getComponent<BeingComponent>()->getName(); // Feedback. std::stringstream targetMsg; std::stringstream userMsg; if (length > 0) { - targetMsg << player->getName() << " muted you for " + targetMsg << playerName << " muted you for " << length << " seconds."; - userMsg << "You muted " << other->getName() + userMsg << "You muted " << otherName << " for " << length << " seconds."; } else { - targetMsg << player->getName() << " unmuted you."; - userMsg << "You unmuted " << other->getName() << "."; + targetMsg << playerName << " unmuted you."; + userMsg << "You unmuted " << otherName << "."; } say(targetMsg.str(), other); say(userMsg.str(), player); @@ -1209,22 +1260,24 @@ static void handleMute(Character *player, std::string &args) std::stringstream msg; if (length > 0) { - msg << "User muted " << other->getName() << " for " << length << " seconds."; + msg << "User muted " << otherName << " for " << length << " seconds."; } else { - msg << "User unmuted " << other->getName(); + msg << "User unmuted " << otherName; } - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_MUTE, msg.str()); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_MUTE, msg.str()); } -static void handleDie(Character *player, std::string &) +static void handleDie(Entity *player, std::string &) { - player->setAttribute(ATTR_HP, 0); + player->getComponent<BeingComponent>()->setAttribute(*player, ATTR_HP, 0); say("You've killed yourself.", player); } -static void handleKill(Character *player, std::string &args) +static void handleKill(Entity *player, std::string &args) { - Character *other; + Entity *other; // get arguments std::string character = getArgument(args); @@ -1238,25 +1291,30 @@ static void handleKill(Character *player, std::string &args) } // kill the player - player->setAttribute(ATTR_HP, 0); + other->getComponent<BeingComponent>()->setAttribute(*player, ATTR_HP, 0); // feedback std::stringstream targetMsg; std::stringstream userMsg; - targetMsg << "You were killed by server command from "<< player->getName() << "."; - userMsg << "You killed " << other->getName() << "."; + targetMsg << "You were killed by server command from " + << player->getComponent<BeingComponent>()->getName() << "."; + userMsg << "You killed " + << other->getComponent<BeingComponent>()->getName() << "."; say(targetMsg.str(), other); say(userMsg.str(), player); // log transaction std::stringstream logMsg; - logMsg << "User killed " << other->getName(); - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_KILL, logMsg.str()); + logMsg << "User killed " + << other->getComponent<BeingComponent>()->getName(); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_KILL, logMsg.str()); } -static void handleKick(Character *player, std::string &args) +static void handleKick(Entity *player, std::string &args) { - Character *other; + Entity *other; // get arguments std::string character = getArgument(args); @@ -1271,22 +1329,30 @@ static void handleKick(Character *player, std::string &args) // send feedback std::stringstream userMsg; - userMsg << "You kicked " << other->getName() << "."; + userMsg << "You kicked " + << other->getComponent<BeingComponent>()->getName() << "."; say(userMsg.str(), player); + + auto *characterComponent = + player->getComponent<CharacterComponent>(); + // disconnect the client MessageOut msg(GPMSG_CONNECT_RESPONSE); msg.writeInt8(ERRMSG_ADMINISTRATIVE_LOGOFF); - other->getClient()->disconnect(msg); + characterComponent->getClient()->disconnect(msg); // log transaction std::stringstream logMsg; - logMsg << "User kicked " << other->getName(); - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_KICK, logMsg.str()); + logMsg << "User kicked " + << other->getComponent<BeingComponent>()->getName(); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_KICK, logMsg.str()); } -static void handleLog(Character *player, std::string &msg) +static void handleLog(Entity *player, std::string &msg) { if (msg.empty()) { @@ -1297,13 +1363,15 @@ static void handleLog(Character *player, std::string &msg) // log transaction std::string logmsg = "[silent] " + msg; - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_LOG, logmsg); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_LOG, logmsg); // send feedback say("Message logged", player); } -static void handleLogsay(Character *player, std::string &msg) +static void handleLogsay(Entity *player, std::string &msg) { if (msg.empty()) { @@ -1316,22 +1384,25 @@ static void handleLogsay(Character *player, std::string &msg) // log transaction std::string logmsg = "[public] " + msg; - accountHandler->sendTransaction(player->getDatabaseID(), TRANS_CMD_LOG, logmsg); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_LOG, logmsg); // send feedback say("Message logged", player); } -static void handleKillMonsters(Character *player, std::string &) +static void handleKillMonsters(Entity *player, std::string &) { const MapComposite *map = player->getMap(); int count = 0; for (BeingIterator it(map->getWholeMapIterator()); it; ++it) { - if ((*it)->getType() == OBJECT_MONSTER && (*it)->getAction() != DEAD) + if ((*it)->getType() == OBJECT_MONSTER && + (*it)->getComponent<BeingComponent>()->getAction() != DEAD) { - (*it)->died(); + (*it)->getComponent<BeingComponent>()->died(**it); count++; } } @@ -1342,11 +1413,12 @@ static void handleKillMonsters(Character *player, std::string &) // log transaction std::string msg = "User killed all monsters on map " + map->getName(); - accountHandler->sendTransaction(player->getDatabaseID(), - TRANS_CMD_KILLMONSTERS, msg); + int databaseId = + player->getComponent<CharacterComponent>()->getDatabaseID(); + accountHandler->sendTransaction(databaseId, TRANS_CMD_KILLMONSTERS, msg); } -static void handleCraft(Character *player, std::string &args) +static void handleCraft(Entity *player, std::string &args) { std::stringstream errMsg; std::list<InventoryItem> recipe; @@ -1419,7 +1491,7 @@ static void handleCraft(Character *player, std::string &args) } } -static void handleGetPos(Character *player, std::string &args) +static void handleGetPos(Entity *player, std::string &args) { std::string character = getArgument(args); if (character.empty()) @@ -1428,19 +1500,19 @@ static void handleGetPos(Character *player, std::string &args) say("Usage: @getpos <character>", player); return; } - Character *other; + Entity *other; other = gameHandler->getCharacterByNameSlow(character); if (!other) { say("Invalid character, or player is offline.", player); return; } - const Point &pos = other->getPosition(); + const Point &pos = other->getComponent<ActorComponent>()->getPosition(); std::stringstream str; str << "The current location of " << character << " is map " - << other->getMapId() + << other->getMap()->getID() << " [" << pos.x << ":" @@ -1449,7 +1521,7 @@ static void handleGetPos(Character *player, std::string &args) say(str.str(), player); } -static void handleSkills(Character *player, std::string &args) +static void handleSkills(Entity *player, std::string &args) { std::string character = getArgument(args); if (character.empty()) @@ -1458,7 +1530,7 @@ static void handleSkills(Character *player, std::string &args) say("Usage: @skills <character>", player); return; } - Character *other; + Entity *other; if (character == "#") other = player; else @@ -1469,9 +1541,16 @@ static void handleSkills(Character *player, std::string &args) return; } - say("List of skills of player '" + other->getName() + "':", player); - std::map<int, int>::const_iterator it = other->getSkillBegin(); - std::map<int, int>::const_iterator it_end = other->getSkillEnd(); + + auto *characterComponent = + player->getComponent<CharacterComponent>(); + + say("List of skills of player '" + + other->getComponent<BeingComponent>()->getName() + "':", player); + std::map<int, int>::const_iterator it = + characterComponent->getSkillBegin(); + std::map<int, int>::const_iterator it_end = + characterComponent->getSkillEnd(); if (it == it_end) { @@ -1488,7 +1567,7 @@ static void handleSkills(Character *player, std::string &args) } } -static void handleEffect(Character *player, std::string &args) +static void handleEffect(Entity *player, std::string &args) { std::vector<std::string> arguments; for (std::string arg = getArgument(args); !arg.empty(); @@ -1500,18 +1579,18 @@ static void handleEffect(Character *player, std::string &args) if (arguments.size() == 1) { int id = utils::stringToInt(arguments[0]); - Effects::show(id, player->getMap(), player); + Effects::show(id, player); } else if (arguments.size() == 2) { int id = utils::stringToInt(arguments[0]); - Character *p = gameHandler->getCharacterByNameSlow(arguments[1]); + Entity *p = gameHandler->getCharacterByNameSlow(arguments[1]); if (!p) { say("Invalid target player.", player); return; } - Effects::show(id, p->getMap(), p); + Effects::show(id, p); } else if (arguments.size() == 3) { @@ -1528,7 +1607,7 @@ static void handleEffect(Character *player, std::string &args) } } -static void handleGiveSpecial(Character *player, std::string &args) +static void handleGiveSpecial(Entity *player, std::string &args) { std::string character = getArgument(args); std::string special = getArgument(args); @@ -1539,7 +1618,7 @@ static void handleGiveSpecial(Character *player, std::string &args) return; } - Character *other; + Entity *other; if (character == "#") other = player; else @@ -1557,14 +1636,15 @@ static void handleGiveSpecial(Character *player, std::string &args) else specialId = specialManager->getId(special); - if (specialId <= 0 || !other->giveSpecial(specialId)) + if (specialId <= 0 || + !other->getComponent<CharacterComponent>()->giveSpecial(specialId)) { say("Invalid special.", player); return; } } -static void handleTakeSpecial(Character *player, std::string &args) +static void handleTakeSpecial(Entity *player, std::string &args) { std::string character = getArgument(args); std::string special = getArgument(args); @@ -1575,7 +1655,7 @@ static void handleTakeSpecial(Character *player, std::string &args) return; } - Character *other; + Entity *other; if (character == "#") other = player; else @@ -1598,14 +1678,14 @@ static void handleTakeSpecial(Character *player, std::string &args) say("Invalid special.", player); return; } - if (!other->takeSpecial(specialId)) + if (!other->getComponent<CharacterComponent>()->takeSpecial(specialId)) { say("Character does not have special.", player); return; } } -static void handleRechargeSpecial(Character *player, std::string &args) +static void handleRechargeSpecial(Entity *player, std::string &args) { std::string character = getArgument(args); std::string special = getArgument(args); @@ -1617,7 +1697,7 @@ static void handleRechargeSpecial(Character *player, std::string &args) return; } - Character *other; + Entity *other; if (character == "#") other = player; else @@ -1657,14 +1737,15 @@ static void handleRechargeSpecial(Character *player, std::string &args) } mana = utils::stringToInt(newMana); } - if (!other->setSpecialMana(specialId, mana)) + if (!other->getComponent<CharacterComponent>() + ->setSpecialMana(specialId, mana)) { say("Character does not have special.", player); return; } } -static void handleListSpecials(Character *player, std::string &args) +static void handleListSpecials(Entity *player, std::string &args) { std::string character = getArgument(args); if (character.empty()) @@ -1674,7 +1755,7 @@ static void handleListSpecials(Character *player, std::string &args) return; } - Character *other; + Entity *other; if (character == "#") other = player; else @@ -1686,9 +1767,13 @@ static void handleListSpecials(Character *player, std::string &args) return; } - say("Specials of character " + other->getName() + ":", player); - for (SpecialMap::const_iterator it = other->getSpecialBegin(), - it_end = other->getSpecialEnd(); it != it_end; ++it) + auto *characterComponent = + other->getComponent<CharacterComponent>(); + + say("Specials of character " + + other->getComponent<BeingComponent>()->getName() + ":", player); + for (SpecialMap::const_iterator it = characterComponent->getSpecialBegin(), + it_end = characterComponent->getSpecialEnd(); it != it_end; ++it) { const SpecialValue &info = it->second; std::stringstream str; @@ -1698,7 +1783,7 @@ static void handleListSpecials(Character *player, std::string &args) } } -void CommandHandler::handleCommand(Character *player, +void CommandHandler::handleCommand(Entity *player, const std::string &command) { // get command type, and arguments diff --git a/src/game-server/commandhandler.h b/src/game-server/commandhandler.h index 5327dda7..c947a4dc 100644 --- a/src/game-server/commandhandler.h +++ b/src/game-server/commandhandler.h @@ -23,14 +23,14 @@ #include <string> -class Character; +class Entity; namespace CommandHandler { /** * Parse and handle the given command. */ - void handleCommand(Character *player, const std::string &command); + void handleCommand(Entity *player, const std::string &command); } #endif //SERVER_COMMANDHANDLER_H diff --git a/src/game-server/component.h b/src/game-server/component.h new file mode 100644 index 00000000..824de2c8 --- /dev/null +++ b/src/game-server/component.h @@ -0,0 +1,59 @@ +/* + * The Mana Server + * Copyright (C) 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/>. + */ + +#ifndef COMPONENT_H +#define COMPONENT_H + +#include <sigc++/trackable.h> + +class Entity; + +enum ComponentType +{ + CT_Actor, + CT_Character, + CT_Being, + CT_Effect, + CT_Fighting, + CT_Item, + CT_Monster, + CT_Npc, + CT_SpawnArea, + CT_TriggerArea, + + ComponentTypeCount +}; + +/** + * A component of an entity. + */ +class Component : public sigc::trackable +{ + public: + virtual ~Component() {} + + /** + * Updates the internal status. The \a entity is the owner of this + * component. + */ + virtual void update(Entity &entity) = 0; +}; + +#endif // COMPONENT_H diff --git a/src/game-server/effect.cpp b/src/game-server/effect.cpp index b7c6c643..42142c5e 100644 --- a/src/game-server/effect.cpp +++ b/src/game-server/effect.cpp @@ -1,6 +1,7 @@ /* * The Mana Server * Copyright (C) 2004-2010 The Mana World Development Team + * Copyright (C) 2012 The Mana Developers * * This file is part of The Mana Server. * @@ -20,29 +21,43 @@ #include "game-server/effect.h" -#include "game-server/mapcomposite.h" +#include "game-server/being.h" +#include "game-server/entity.h" #include "game-server/state.h" -void Effect::update() +void EffectComponent::update(Entity &entity) { if (mHasBeenShown) - GameState::enqueueRemove(this); + GameState::enqueueRemove(&entity); } namespace Effects { void show(int id, MapComposite *map, const Point &pos) { - Effect *effect = new Effect(id); + Entity *effect = new Entity(OBJECT_EFFECT); + auto *actorComponent = new ActorComponent(*effect); + effect->addComponent(actorComponent); + effect->addComponent(new EffectComponent(id)); effect->setMap(map); - effect->setPosition(pos); + actorComponent->setPosition(*effect, pos); + GameState::enqueueInsert(effect); } - void show(int id, MapComposite *map, Being *b) + + void show(int id, Entity *b) { - Effect *effect = new Effect(id); - effect->setMap(map); - if (effect->setBeing(b)) - GameState::enqueueInsert(effect); + EffectComponent *effectComponent = new EffectComponent(id); + effectComponent->setBeing(b); + + Entity *effect = new Entity(OBJECT_EFFECT); + auto *actorComponent = new ActorComponent(*effect); + effect->addComponent(actorComponent); + effect->addComponent(effectComponent); + effect->setMap(b->getMap()); + const Point &point = b->getComponent<ActorComponent>()->getPosition(); + actorComponent->setPosition(*effect, point); + + GameState::enqueueInsert(effect); } } diff --git a/src/game-server/effect.h b/src/game-server/effect.h index 2e22d46e..556d6acb 100644 --- a/src/game-server/effect.h +++ b/src/game-server/effect.h @@ -1,6 +1,7 @@ /* * The Mana Server * Copyright (C) 2004-2010 The Mana World Development Team + * Copyright (C) 2012 The Mana Developers * * This file is part of The Mana Server. * @@ -21,53 +22,47 @@ #ifndef EFFECT_H #define EFFECT_H -#include "game-server/actor.h" -#include "game-server/being.h" +#include "game-server/component.h" -class Effect : public Actor +class Entity; +class MapComposite; +class Point; + +class EffectComponent : public Component { public: - Effect(int id) - : Actor(OBJECT_EFFECT) - , mEffectId(id) + static const ComponentType type = CT_Effect; + + EffectComponent(int id) + : mEffectId(id) , mHasBeenShown(false) - , mBeing(NULL) + , mBeing(0) {} int getEffectId() const { return mEffectId; } - Being *getBeing() const + Entity *getBeing() const { return mBeing; } /** * Removes effect after it has been shown. */ - virtual void update(); + void update(Entity &entity); /** * Called when the object has been shown to a player in the state loop. */ - void show() + void setShown() { mHasBeenShown = true; } - - bool setBeing(Being *b) - { - if (b) - { - setPosition(b->getPosition()); - mBeing = b; - return true; - } else { - return false; - } - } + void setBeing(Entity *b) + { mBeing = b; } private: int mEffectId; bool mHasBeenShown; - Being *mBeing; + Entity *mBeing; }; @@ -77,7 +72,7 @@ namespace Effects * Convenience methods to show an effect. */ void show(int id, MapComposite *map, const Point &pos); - void show(int id, MapComposite *map, Being *b); + void show(int id, Entity *b); // TODO: get this in sync with effects.xml enum { @@ -85,4 +80,4 @@ namespace Effects }; } -#endif +#endif // EFFECT_H diff --git a/src/game-server/entity.cpp b/src/game-server/entity.cpp index 6cb61e58..b40ac442 100644 --- a/src/game-server/entity.cpp +++ b/src/game-server/entity.cpp @@ -1,6 +1,7 @@ /* * The Mana Server * Copyright (C) 2007-2010 The Mana World Development Team + * Copyright (C) 2012 The Mana Developers * * This file is part of The Mana Server. * @@ -19,3 +20,27 @@ */ #include "game-server/entity.h" + +Entity::Entity(EntityType type, MapComposite *map) : + mMap(map), + mType(type) +{ + for (int i = 0; i < ComponentTypeCount; ++i) + mComponents[i] = nullptr; +} + +Entity::~Entity() +{ + for (int i = 0; i < ComponentTypeCount; ++i) + delete mComponents[i]; +} + +/** + * Updates the internal status. By default, calls update on all its components. + */ +void Entity::update() +{ + for (int i = 0; i < ComponentTypeCount; ++i) + if (mComponents[i]) + mComponents[i]->update(*this); +} diff --git a/src/game-server/entity.h b/src/game-server/entity.h index 91f13699..bb76e440 100644 --- a/src/game-server/entity.h +++ b/src/game-server/entity.h @@ -1,6 +1,7 @@ /* * The Mana Server * Copyright (C) 2004-2010 The Mana World Development Team + * Copyright (C) 2012 The Mana Developers * * This file is part of The Mana Server. * @@ -23,79 +24,162 @@ #include "common/manaserv_protocol.h" -#include <set> +#include "game-server/component.h" #include <sigc++/signal.h> #include <sigc++/trackable.h> +#include <cassert> + using namespace ManaServ; class MapComposite; /** - * Base class for in-game objects. Knows only its type and the map it resides - * on. Provides listeners. + * Base class for in-game objects. + * + * Knows its type, the map it resides on and is host to a number of optional + * components. */ class Entity : public sigc::trackable { public: - Entity(EntityType type, MapComposite *map = 0) - : mMap(map), - mType(type) - {} - - virtual ~Entity() {} - - /** - * Gets type of this entity. - * - * @return the type of this entity. - */ - EntityType getType() const - { return mType; } - - /** - * Returns whether this entity is visible on the map or not. (Actor) - */ - bool isVisible() const - { return mType != OBJECT_OTHER; } - - /** - * Returns whether this entity can move on the map or not. (Actor) - */ - bool canMove() const - { return mType == OBJECT_CHARACTER || mType == OBJECT_MONSTER || - mType == OBJECT_NPC; } - - /** - * Returns whether this entity can fight or not. (Being) - */ - bool canFight() const - { return mType == OBJECT_CHARACTER || mType == OBJECT_MONSTER; } - - /** - * Updates the internal status. - */ - virtual void update() = 0; - - /** - * Gets the map this entity is located on. - */ - MapComposite *getMap() const - { return mMap; } - - /** - * Sets the map this entity is located on. - */ - virtual void setMap(MapComposite *map) - { mMap = map; } + Entity(EntityType type, MapComposite *map = nullptr); + + virtual ~Entity(); + + EntityType getType() const; + + template <class T> void addComponent(T *component); + template <class T> T *getComponent() const; + template <class T> T *findComponent() const; + template <class T> bool hasComponent() const; + + bool isVisible() const; + bool canMove() const; + bool canFight() const; + + virtual void update(); + + MapComposite *getMap() const; + void setMap(MapComposite *map); sigc::signal<void, Entity *> signal_inserted; sigc::signal<void, Entity *> signal_removed; + sigc::signal<void, Entity *> signal_map_changed; private: + Component *getComponent(ComponentType type) const; + MapComposite *mMap; /**< Map the entity is on */ EntityType mType; /**< Type of this entity. */ + + Component *mComponents[ComponentTypeCount]; }; +/** + * Gets type of this entity. + * + * @return the type of this entity. + */ +inline EntityType Entity::getType() const +{ + return mType; +} + +/** + * Adds a component. Only one component of a given type can be added. + * Entity takes ownership of \a component. + */ +template <class T> +inline void Entity::addComponent(T *component) +{ + mComponents[T::type] = component; +} + +/** + * Returns the component of the given type, or 0 when no such component + * was set. + */ +inline Component *Entity::getComponent(ComponentType type) const +{ + return mComponents[type]; +} + +/** + * Get a component by its class. Avoids the need for doing a static-cast in the + * calling code. + * + * Asserts that the component is actually there. Use findComponent instead if + * you're not sure whether the requested component is actually present. + */ +template <class T> +inline T *Entity::getComponent() const +{ + T *component = static_cast<T*>(getComponent(T::type)); + assert(component); + return component; +} + +/** + * Finds a component by its class. Returns 0 when the entity does not have the + * requested component. + */ +template <class T> +inline T *Entity::findComponent() const +{ + return static_cast<T*>(getComponent(T::type)); +} + +/** + * Returns whether this class has a certain component. + */ +template <class T> +inline bool Entity::hasComponent() const +{ + return getComponent(T::type) != nullptr; +} + +/** + * Returns whether this entity is visible on the map or not. (Actor) + */ +inline bool Entity::isVisible() const +{ + return mType != OBJECT_OTHER; +} + +/** + * Returns whether this entity can move on the map or not. (Actor) + */ +inline bool Entity::canMove() const +{ + return mType == OBJECT_CHARACTER || mType == OBJECT_MONSTER || + mType == OBJECT_NPC; +} + +/** + * Returns whether this entity can fight or not. (Being) + */ +inline bool Entity::canFight() const +{ + return mType == OBJECT_CHARACTER || mType == OBJECT_MONSTER; +} + +/** + * Gets the map this entity is located on. + */ +inline MapComposite *Entity::getMap() const +{ + return mMap; +} + +/** + * Sets the map this entity is located on. + */ +inline void Entity::setMap(MapComposite *map) +{ + mMap = map; + signal_map_changed.emit(this); +} + #endif // ENTITY_H diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp index 3b356a5c..95663168 100644 --- a/src/game-server/gamehandler.cpp +++ b/src/game-server/gamehandler.cpp @@ -27,6 +27,7 @@ #include "common/transaction.h" #include "game-server/accountconnection.h" #include "game-server/buysell.h" +#include "game-server/combatcomponent.h" #include "game-server/commandhandler.h" #include "game-server/emotemanager.h" #include "game-server/inventory.h" @@ -70,27 +71,28 @@ void GameHandler::computerDisconnected(NetComputer *comp) { mTokenCollector.deletePendingClient(&computer); } - else if (Character *ch = computer.character) + else if (Entity *ch = computer.character) { accountHandler->sendCharacterData(ch); - ch->disconnected(); + ch->getComponent<CharacterComponent>()->disconnected(*ch); delete ch; } delete &computer; } -void GameHandler::kill(Character *ch) +void GameHandler::kill(Entity *ch) { - GameClient *client = ch->getClient(); + auto *component = ch->getComponent<CharacterComponent>(); + GameClient *client = component->getClient(); assert(client); - client->character = NULL; + client->character = nullptr; client->status = CLIENT_LOGIN; - ch->setClient(0); + component->setClient(0); } -void GameHandler::prepareServerChange(Character *ch) +void GameHandler::prepareServerChange(Entity *ch) { - GameClient *client = ch->getClient(); + GameClient *client = ch->getComponent<CharacterComponent>()->getClient(); assert(client); client->status = CLIENT_CHANGE_SERVER; } @@ -103,14 +105,16 @@ void GameHandler::completeServerChange(int id, const std::string &token, { GameClient *c = static_cast< GameClient * >(*i); if (c->status == CLIENT_CHANGE_SERVER && - c->character->getDatabaseID() == id) + c->character->getComponent<CharacterComponent>() + ->getDatabaseID() == id) { MessageOut msg(GPMSG_PLAYER_SERVER_CHANGE); msg.writeString(token, MAGIC_TOKEN_LENGTH); msg.writeString(address); msg.writeInt16(port); c->send(msg); - c->character->disconnected(); + c->character->getComponent<CharacterComponent>()->disconnected( + *c->character); delete c->character; c->character = NULL; c->status = CLIENT_LOGIN; @@ -125,58 +129,66 @@ void GameHandler::updateCharacter(int charid, int partyid) i_end = clients.end(); i != i_end; ++i) { GameClient *c = static_cast< GameClient * >(*i); - if (c->character->getDatabaseID() == charid) - { - c->character->setParty(partyid); - } + auto *characterComponent = + c->character->getComponent<CharacterComponent>(); + + if (characterComponent->getDatabaseID() == charid) + characterComponent->setParty(partyid); } } -static Actor *findActorNear(Actor *p, int id) +static Entity *findActorNear(Entity *p, int id) { MapComposite *map = p->getMap(); - const Point &ppos = p->getPosition(); + const Point &ppos = p->getComponent<ActorComponent>()->getPosition(); // See map.h for tiles constants const int pixelDist = DEFAULT_TILE_LENGTH * TILES_TO_BE_NEAR; for (ActorIterator i(map->getAroundPointIterator(ppos, pixelDist)); i; ++i) { - Actor *a = *i; - if (a->getPublicID() != id) + Entity *a = *i; + if (a->getComponent<ActorComponent>()->getPublicID() != id) continue; - return ppos.inRangeOf(a->getPosition(), pixelDist) ? a : 0; + return ppos.inRangeOf(a->getComponent<ActorComponent>()->getPosition(), + pixelDist) ? a : 0; } return 0; } -static Being *findBeingNear(Actor *p, int id) +static Entity *findBeingNear(Entity *p, int id) { MapComposite *map = p->getMap(); - const Point &ppos = p->getPosition(); + const Point &ppos = p->getComponent<ActorComponent>()->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) + Entity *b = *i; + if (b->getComponent<ActorComponent>()->getPublicID() != id) continue; - return ppos.inRangeOf(b->getPosition(), pixelDist) ? b : 0; + return ppos.inRangeOf(b->getComponent<ActorComponent>()->getPosition(), + pixelDist) ? b : 0; } return 0; } -static Character *findCharacterNear(Actor *p, int id) +static Entity *findCharacterNear(Entity *p, int id) { MapComposite *map = p->getMap(); - const Point &ppos = p->getPosition(); + const Point &ppos = p->getComponent<ActorComponent>()->getPosition(); // See map.h for tiles constants const int pixelDist = DEFAULT_TILE_LENGTH * TILES_TO_BE_NEAR; for (CharacterIterator i(map->getAroundPointIterator(ppos, pixelDist)); i; ++i) { - Character *c = *i; - if (c->getPublicID() != id) + Entity *c = *i; + if (c->getComponent<ActorComponent>()->getPublicID() != id) continue; - return ppos.inRangeOf(c->getPosition(), pixelDist) ? c : 0; + + if (ppos.inRangeOf(c->getComponent<ActorComponent>()->getPosition(), + pixelDist)) + return c; + + return 0; } return 0; } @@ -292,7 +304,8 @@ void GameHandler::processMessage(NetComputer *computer, MessageIn &message) case PGMSG_RESPAWN: // plausibility check is done by character class - client.character->respawn(); + client.character->getComponent<CharacterComponent>()->respawn( + *client.character); break; case PGMSG_NPC_POST_SEND: @@ -314,26 +327,34 @@ void GameHandler::processMessage(NetComputer *computer, MessageIn &message) } } -void GameHandler::sendTo(Character *beingPtr, MessageOut &msg) +void GameHandler::sendTo(Entity *beingPtr, MessageOut &msg) +{ + GameClient *client = beingPtr->getComponent<CharacterComponent>() + ->getClient(); + sendTo(client, msg); +} + +void GameHandler::sendTo(GameClient *client, MessageOut &msg) { - GameClient *client = beingPtr->getClient(); assert(client && client->status == CLIENT_CONNECTED); client->send(msg); } -void GameHandler::addPendingCharacter(const std::string &token, Character *ch) +void GameHandler::addPendingCharacter(const std::string &token, Entity *ch) { /* First, check if the character is already on the map. This may happen if a client just lost its connection, and logged to the account server again, yet the game server has not yet detected the lost connection. */ - int id = ch->getDatabaseID(); + int id = ch->getComponent<CharacterComponent>()->getDatabaseID(); for (NetComputers::const_iterator i = clients.begin(), i_end = clients.end(); i != i_end; ++i) { GameClient *c = static_cast< GameClient * >(*i); - Character *old_ch = c->character; - if (old_ch && old_ch->getDatabaseID() == id) + Entity *old_ch = c->character; + const int oldId = old_ch->getComponent<CharacterComponent>() + ->getDatabaseID(); + if (old_ch && oldId == id) { if (c->status != CLIENT_CONNECTED) { @@ -359,12 +380,15 @@ void GameHandler::addPendingCharacter(const std::string &token, Character *ch) mTokenCollector.addPendingConnect(token, ch); } -void GameHandler::tokenMatched(GameClient *computer, Character *character) +void GameHandler::tokenMatched(GameClient *computer, Entity *character) { computer->character = character; computer->status = CLIENT_CONNECTED; - character->setClient(computer); + auto *characterComponent = + character->getComponent<CharacterComponent>(); + + characterComponent->setClient(computer); MessageOut result(GPMSG_CONNECT_RESPONSE); @@ -377,14 +401,14 @@ void GameHandler::tokenMatched(GameClient *computer, Character *character) return; } // Trigger login script bind - character->triggerLoginCallback(); + characterComponent->triggerLoginCallback(*character); result.writeInt8(ERRMSG_OK); computer->send(result); // Force sending the whole character to the client. Inventory(character).sendFull(); - character->modifiedAllAttribute(); + characterComponent->modifiedAllAttributes(*character); } void GameHandler::deletePendingClient(GameClient *computer) @@ -400,19 +424,19 @@ void GameHandler::deletePendingClient(GameClient *computer) computer->disconnect(msg); } -void GameHandler::deletePendingConnect(Character *character) +void GameHandler::deletePendingConnect(Entity *character) { delete character; } -Character *GameHandler::getCharacterByNameSlow(const std::string &name) const +Entity *GameHandler::getCharacterByNameSlow(const std::string &name) const { for (NetComputers::const_iterator i = clients.begin(), i_end = clients.end(); i != i_end; ++i) { GameClient *c = static_cast< GameClient * >(*i); - Character *ch = c->character; - if (ch && ch->getName() == name && + Entity *ch = c->character; + if (ch && ch->getComponent<BeingComponent>()->getName() == name && c->status == CLIENT_CONNECTED) { return ch; @@ -432,13 +456,13 @@ void GameHandler::handleSay(GameClient &client, MessageIn &message) CommandHandler::handleCommand(client.character, say); return; } - if (!client.character->isMuted()) + if (!client.character->getComponent<CharacterComponent>()->isMuted()) { GameState::sayAround(client.character, say); } else { - GameState::sayTo(client.character, NULL, + GameState::sayTo(client.character, nullptr, "You are not allowed to talk right now."); } } @@ -446,29 +470,29 @@ void GameHandler::handleSay(GameClient &client, MessageIn &message) void GameHandler::handleNpc(GameClient &client, MessageIn &message) { int id = message.readInt16(); - Actor *actor = findActorNear(client.character, id); + Entity *actor = findActorNear(client.character, id); if (!actor || actor->getType() != OBJECT_NPC) { sendNpcError(client, id, "Not close enough to NPC\n"); return; } - - NPC *npc = static_cast<NPC *>(actor); switch (message.getId()) { case PGMSG_NPC_SELECT: - npc->select(client.character, message.readInt8()); + Npc::integerReceived(client.character, message.readInt8()); break; case PGMSG_NPC_NUMBER: - npc->integerReceived(client.character, message.readInt32()); + Npc::integerReceived(client.character, message.readInt32()); break; case PGMSG_NPC_STRING: - npc->stringReceived(client.character, message.readString()); + Npc::stringReceived(client.character, message.readString()); break; case PGMSG_NPC_TALK: + Npc::start(actor, client.character); + break; case PGMSG_NPC_TALK_NEXT: default: - npc->prompt(client.character, message.getId() == PGMSG_NPC_TALK); + Npc::resume(client.character); break; } } @@ -477,7 +501,8 @@ void GameHandler::handlePickup(GameClient &client, MessageIn &message) { const int x = message.readInt16(); const int y = message.readInt16(); - const Point ppos = client.character->getPosition(); + const Point ppos = + client.character->getComponent<ActorComponent>()->getPosition(); // TODO: use a less arbitrary value. if (std::abs(x - ppos.x) + std::abs(y - ppos.y) < 48) @@ -486,17 +511,19 @@ void GameHandler::handlePickup(GameClient &client, MessageIn &message) Point ipos(x, y); for (FixedActorIterator i(map->getAroundPointIterator(ipos, 0)); i; ++i) { - Actor *o = *i; - Point opos = o->getPosition(); + Entity *o = *i; + Point opos = o->getComponent<ActorComponent>()->getPosition(); + if (o->getType() == OBJECT_ITEM && opos.x == x && opos.y == y) { - Item *item = static_cast< Item * >(o); + ItemComponent *item = o->getComponent<ItemComponent>(); ItemClass *ic = item->getItemClass(); int amount = item->getAmount(); + if (!Inventory(client.character).insert(ic->getDatabaseID(), - amount)) + amount)) { - GameState::remove(item); + GameState::remove(o); // We only do this when items are to be kept in memory // between two server restart. @@ -504,17 +531,19 @@ void GameHandler::handlePickup(GameClient &client, MessageIn &message) { // Remove the floor item from map accountHandler->removeFloorItems(map->getID(), - ic->getDatabaseID(), - amount, x, y); + ic->getDatabaseID(), + amount, x, y); } // log transaction std::stringstream str; str << "User picked up item " << ic->getDatabaseID() << " at " << opos.x << "x" << opos.y; + auto *characterComponent = client.character + ->getComponent<CharacterComponent>(); accountHandler->sendTransaction( - client.character->getDatabaseID(), - TRANS_ITEM_PICKUP, str.str() + characterComponent->getDatabaseID(), + TRANS_ITEM_PICKUP, str.str() ); } break; @@ -525,7 +554,7 @@ void GameHandler::handlePickup(GameClient &client, MessageIn &message) void GameHandler::handleUseItem(GameClient &client, MessageIn &message) { - if (client.character->getAction() == DEAD) + if (client.character->getComponent<BeingComponent>()->getAction() == DEAD) return; const int slot = message.readInt16(); @@ -539,7 +568,9 @@ void GameHandler::handleUseItem(GameClient &client, MessageIn &message) std::stringstream str; str << "User activated item " << ic->getDatabaseID() << " from slot " << slot; - accountHandler->sendTransaction(client.character->getDatabaseID(), + auto *characterComponent = client.character + ->getComponent<CharacterComponent>(); + accountHandler->sendTransaction(characterComponent->getDatabaseID(), TRANS_ITEM_USED, str.str()); if (ic->useTrigger(client.character, ITT_ACTIVATE)) inv.removeFromSlot(slot, 1); @@ -556,34 +587,36 @@ void GameHandler::handleDrop(GameClient &client, MessageIn &message) if (ItemClass *ic = itemManager->getItem(inv.getItem(slot))) { int nb = inv.removeFromSlot(slot, amount); - Item *item = new Item(ic, amount - nb); - item->setMap(client.character->getMap()); - item->setPosition(client.character->getPosition()); - if (!GameState::insert(item)) + MapComposite *map = client.character->getMap(); + const Point &pos = client.character->getComponent<ActorComponent>() + ->getPosition(); + + Entity *item = Item::create(map, pos, ic, amount - nb); + + if (!GameState::insertOrDelete(item)) { // The map is full. Put back into inventory. inv.insert(ic->getDatabaseID(), amount - nb); - delete item; return; } - Point pt = client.character->getPosition(); - // We store the item in database only when the floor items are meant // to be persistent between two server restarts. if (!Configuration::getValue("game_floorItemDecayTime", 0)) { // Create the floor item on map accountHandler->createFloorItems(client.character->getMap()->getID(), - ic->getDatabaseID(), - amount, pt.x, pt.y); + ic->getDatabaseID(), + amount, pos.x, pos.y); } // log transaction std::stringstream str; str << "User dropped item " << ic->getDatabaseID() - << " at " << pt.x << "x" << pt.y; - accountHandler->sendTransaction(client.character->getDatabaseID(), + << " at " << pos.x << "x" << pos.y; + auto *characterComponent = client.character + ->getComponent<CharacterComponent>(); + accountHandler->sendTransaction(characterComponent->getDatabaseID(), TRANS_ITEM_DROP, str.str()); } } @@ -594,7 +627,8 @@ void GameHandler::handleWalk(GameClient &client, MessageIn &message) const int y = message.readInt16(); Point dst(x, y); - client.character->setDestination(dst); + client.character->getComponent<BeingComponent>()->setDestination( + *client.character, dst); } void GameHandler::handleEquip(GameClient &client, MessageIn &message) @@ -632,57 +666,72 @@ void GameHandler::handleMoveItem(GameClient &client, MessageIn &message) std::stringstream str; str << "User moved item " << " from slot " << slot1 << " to slot " << slot2; - accountHandler->sendTransaction(client.character->getDatabaseID(), + auto *characterComponent = + client.character->getComponent<CharacterComponent>(); + accountHandler->sendTransaction(characterComponent->getDatabaseID(), TRANS_ITEM_MOVE, str.str()); } void GameHandler::handleAttack(GameClient &client, MessageIn &message) { int id = message.readInt16(); - LOG_DEBUG("Character " << client.character->getPublicID() - << " attacked being " << id); + const int publicId = + client.character->getComponent<ActorComponent>()->getPublicID(); + LOG_DEBUG("Character " << publicId << " attacked being " << id); - Being *being = findBeingNear(client.character, id); + Entity *being = findBeingNear(client.character, id); if (being && being->getType() != OBJECT_NPC) { - client.character->setTarget(being); - client.character->setAction(ATTACK); + client.character->getComponent<CombatComponent>()->setTarget(being); + client.character->getComponent<BeingComponent>()->setAction( + *client.character, ATTACK); } } void GameHandler::handleUseSpecialOnBeing(GameClient &client, MessageIn &message) { - if (client.character->getAction() == DEAD) + if (client.character->getComponent<BeingComponent>()->getAction() == DEAD) return; const int specialID = message.readInt8(); const int targetID = message.readInt16(); // 0 when no target is selected - Being *being = 0; + Entity *being = 0; if (targetID != 0) being = findBeingNear(client.character, targetID); - LOG_DEBUG("Character " << client.character->getPublicID() + + const int publicId = + client.character->getComponent<ActorComponent>()->getPublicID(); + LOG_DEBUG("Character " << publicId << " tries to use his special attack " << specialID); - client.character->useSpecialOnBeing(specialID, being); + auto *characterComponent = client.character + ->getComponent<CharacterComponent>(); + characterComponent->useSpecialOnBeing(*client.character, specialID, being); } void GameHandler::handleUseSpecialOnPoint(GameClient &client, MessageIn &message) { - if (client.character->getAction() == DEAD) + if (client.character->getComponent<BeingComponent>()->getAction() == DEAD) return; const int specialID = message.readInt8(); const int x = message.readInt16(); const int y = message.readInt16(); - LOG_DEBUG("Character " << client.character->getPublicID() + const int publicId = + client.character->getComponent<ActorComponent>()->getPublicID(); + LOG_DEBUG("Character " << publicId << " tries to use his special attack " << specialID); - client.character->useSpecialOnPoint(specialID, x, y); + auto *characterComponent = client.character + ->getComponent<CharacterComponent>(); + characterComponent->useSpecialOnPoint(*client.character, specialID, x, y); } void GameHandler::handleActionChange(GameClient &client, MessageIn &message) { + auto *beingComponent = client.character->getComponent<BeingComponent>(); + const BeingAction action = (BeingAction) message.readInt8(); - const BeingAction current = (BeingAction) client.character->getAction(); + const BeingAction current = (BeingAction) beingComponent->getAction(); bool logActionChange = true; switch (action) @@ -690,14 +739,14 @@ void GameHandler::handleActionChange(GameClient &client, MessageIn &message) case STAND: if (current == SIT) { - client.character->setAction(STAND); + beingComponent->setAction(*client.character, STAND); logActionChange = false; } break; case SIT: if (current == STAND) { - client.character->setAction(SIT); + beingComponent->setAction(*client.character, SIT); logActionChange = false; } break; @@ -711,7 +760,11 @@ void GameHandler::handleActionChange(GameClient &client, MessageIn &message) // log transaction std::stringstream str; str << "User changed action from " << current << " to " << action; - accountHandler->sendTransaction(client.character->getDatabaseID(), + + auto *characterComponent = + client.character->getComponent<CharacterComponent>(); + + accountHandler->sendTransaction(characterComponent->getDatabaseID(), TRANS_ACTION_CHANGE, str.str()); } @@ -720,7 +773,8 @@ void GameHandler::handleActionChange(GameClient &client, MessageIn &message) void GameHandler::handleDirectionChange(GameClient &client, MessageIn &message) { const BeingDirection direction = (BeingDirection) message.readInt8(); - client.character->setDirection(direction); + client.character->getComponent<BeingComponent>() + ->setDirection(*client.character, direction); } void GameHandler::handleDisconnect(GameClient &client, MessageIn &message) @@ -730,19 +784,21 @@ void GameHandler::handleDisconnect(GameClient &client, MessageIn &message) MessageOut result(GPMSG_DISCONNECT_RESPONSE); result.writeInt8(ERRMSG_OK); // It is, when control reaches here + auto *characterComponent = + client.character->getComponent<CharacterComponent>(); + if (reconnectAccount) { std::string magic_token(utils::getMagicToken()); result.writeString(magic_token, MAGIC_TOKEN_LENGTH); // No accountserver data, the client should remember that accountHandler->playerReconnectAccount( - client.character->getDatabaseID(), + characterComponent->getDatabaseID(), magic_token); } accountHandler->sendCharacterData(client.character); - // Done with the character, also handle possible respawn case - client.character->disconnected(); + characterComponent->disconnected(*client.character); delete client.character; client.character = 0; client.status = CLIENT_LOGIN; @@ -754,12 +810,15 @@ void GameHandler::handleTradeRequest(GameClient &client, MessageIn &message) { const int id = message.readInt16(); - if (Trade *t = client.character->getTrading()) + auto *characterComponent = + client.character->getComponent<CharacterComponent>(); + + if (Trade *t = characterComponent->getTrading()) if (t->request(client.character, id)) return; - Character *q = findCharacterNear(client.character, id); - if (!q || q->isBusy()) + Entity *q = findCharacterNear(client.character, id); + if (!q || characterComponent->isBusy()) { client.send(MessageOut(GPMSG_TRADE_CANCEL)); return; @@ -769,15 +828,21 @@ void GameHandler::handleTradeRequest(GameClient &client, MessageIn &message) // log transaction std::string str; - str = "User requested trade with " + q->getName(); - accountHandler->sendTransaction(client.character->getDatabaseID(), + str = "User requested trade with " + q->getComponent<BeingComponent>() + ->getName(); + accountHandler->sendTransaction(characterComponent->getDatabaseID(), TRANS_TRADE_REQUEST, str); } void GameHandler::handleTrade(GameClient &client, MessageIn &message) { + auto *characterComponent = + client.character->getComponent<CharacterComponent>(); + + int databaseId = characterComponent->getDatabaseID(); + std::stringstream str; - Trade *t = client.character->getTrading(); + Trade *t = characterComponent->getTrading(); if (!t) return; @@ -792,7 +857,7 @@ void GameHandler::handleTrade(GameClient &client, MessageIn &message) case PGMSG_TRADE_AGREED: t->agree(client.character); // log transaction - accountHandler->sendTransaction(client.character->getDatabaseID(), + accountHandler->sendTransaction(databaseId, TRANS_TRADE_END, "User finished trading"); break; @@ -802,7 +867,7 @@ void GameHandler::handleTrade(GameClient &client, MessageIn &message) t->setMoney(client.character, money); // log transaction str << "User added " << money << " money to trade."; - accountHandler->sendTransaction(client.character->getDatabaseID(), + accountHandler->sendTransaction(databaseId, TRANS_TRADE_MONEY, str.str()); } break; case PGMSG_TRADE_ADD_ITEM: @@ -811,7 +876,7 @@ void GameHandler::handleTrade(GameClient &client, MessageIn &message) t->addItem(client.character, slot, message.readInt8()); // log transaction str << "User add item from slot " << slot; - accountHandler->sendTransaction(client.character->getDatabaseID(), + accountHandler->sendTransaction(databaseId, TRANS_TRADE_ITEM, str.str()); } break; } @@ -819,7 +884,8 @@ void GameHandler::handleTrade(GameClient &client, MessageIn &message) void GameHandler::handleNpcBuySell(GameClient &client, MessageIn &message) { - BuySell *t = client.character->getBuySell(); + BuySell *t = client.character->getComponent<CharacterComponent>() + ->getBuySell(); if (!t) return; const int id = message.readInt16(); @@ -829,9 +895,13 @@ void GameHandler::handleNpcBuySell(GameClient &client, MessageIn &message) void GameHandler::handleRaiseAttribute(GameClient &client, MessageIn &message) { + auto *characterComponent = + client.character->getComponent<CharacterComponent>(); + const int attribute = message.readInt16(); AttribmodResponseCode retCode; - retCode = client.character->useCharacterPoint(attribute); + retCode = characterComponent->useCharacterPoint(*client.character, + attribute); MessageOut result(GPMSG_RAISE_ATTRIBUTE_RESPONSE); result.writeInt8(retCode); @@ -841,23 +911,27 @@ void GameHandler::handleRaiseAttribute(GameClient &client, MessageIn &message) if (retCode == ATTRIBMOD_OK) { accountHandler->updateCharacterPoints( - client.character->getDatabaseID(), - client.character->getCharacterPoints(), - client.character->getCorrectionPoints()); + characterComponent->getDatabaseID(), + characterComponent->getCharacterPoints(), + characterComponent->getCorrectionPoints()); // log transaction std::stringstream str; str << "User increased attribute " << attribute; - accountHandler->sendTransaction(client.character->getDatabaseID(), + accountHandler->sendTransaction(characterComponent->getDatabaseID(), TRANS_ATTR_INCREASE, str.str()); } } void GameHandler::handleLowerAttribute(GameClient &client, MessageIn &message) { + auto *characterComponent = + client.character->getComponent<CharacterComponent>(); + const int attribute = message.readInt32(); AttribmodResponseCode retCode; - retCode = client.character->useCorrectionPoint(attribute); + retCode = characterComponent->useCorrectionPoint(*client.character, + attribute); MessageOut result(GPMSG_LOWER_ATTRIBUTE_RESPONSE); result.writeInt8(retCode); @@ -867,14 +941,14 @@ void GameHandler::handleLowerAttribute(GameClient &client, MessageIn &message) if (retCode == ATTRIBMOD_OK) { accountHandler->updateCharacterPoints( - client.character->getDatabaseID(), - client.character->getCharacterPoints(), - client.character->getCorrectionPoints()); + characterComponent->getDatabaseID(), + characterComponent->getCharacterPoints(), + characterComponent->getCorrectionPoints()); // log transaction std::stringstream str; str << "User decreased attribute " << attribute; - accountHandler->sendTransaction(client.character->getDatabaseID(), + accountHandler->sendTransaction(characterComponent->getDatabaseID(), TRANS_ATTR_DECREASE, str.str()); } } @@ -892,24 +966,26 @@ void GameHandler::handlePartyInvite(GameClient &client, MessageIn &message) const int visualRange = Configuration::getValue("game_visualRange", 448); std::string invitee = message.readString(); - if (invitee == client.character->getName()) + if (invitee == client.character->getComponent<BeingComponent>()->getName()) return; for (CharacterIterator it(map->getWholeMapIterator()); it; ++it) { - if ((*it)->getName() == invitee) + if ((*it)->getComponent<BeingComponent>()->getName() == invitee) { // calculate if the invitee is within the visual range - const int xInviter = client.character->getPosition().x; - const int yInviter = client.character->getPosition().y; - const int xInvitee = (*it)->getPosition().x; - const int yInvitee = (*it)->getPosition().y; - const int dx = std::abs(xInviter - xInvitee); - const int dy = std::abs(yInviter - yInvitee); + auto *inviterComponent = + client.character->getComponent<ActorComponent>(); + auto *inviteeComponent = (*it)->getComponent<ActorComponent>(); + const Point &inviterPosition = inviterComponent->getPosition(); + const Point &inviteePosition = inviteeComponent->getPosition(); + const int dx = std::abs(inviterPosition.x - inviteePosition.x); + const int dy = std::abs(inviterPosition.y - inviteePosition.y); if (visualRange > std::max(dx, dy)) { MessageOut out(GCMSG_PARTY_INVITE); - out.writeString(client.character->getName()); + out.writeString(client.character + ->getComponent<BeingComponent>()->getName()); out.writeString(invitee); accountHandler->send(out); return; @@ -928,7 +1004,8 @@ void GameHandler::handleTriggerEmoticon(GameClient &client, MessageIn &message) { const int id = message.readInt16(); if (emoteManager->isIdAvailable(id)) - client.character->triggerEmote(id); + client.character->getComponent<BeingComponent>()->triggerEmote( + *client.character, id); } void GameHandler::sendNpcError(GameClient &client, int id, diff --git a/src/game-server/gamehandler.h b/src/game-server/gamehandler.h index c05e0087..d66caf52 100644 --- a/src/game-server/gamehandler.h +++ b/src/game-server/gamehandler.h @@ -21,11 +21,12 @@ #ifndef SERVER_GAMEHANDLER_H #define SERVER_GAMEHANDLER_H -#include "game-server/character.h" #include "net/connectionhandler.h" #include "net/netcomputer.h" #include "utils/tokencollector.h" +class Entity; + enum { CLIENT_LOGIN = 0, @@ -37,8 +38,8 @@ enum struct GameClient: NetComputer { GameClient(ENetPeer *peer) - : NetComputer(peer), character(NULL), status(CLIENT_LOGIN) {} - Character *character; + : NetComputer(peer), character(nullptr), status(CLIENT_LOGIN) {} + Entity *character; int status; }; @@ -58,17 +59,18 @@ class GameHandler: public ConnectionHandler /** * Sends message to the given character. */ - void sendTo(Character *, MessageOut &msg); + void sendTo(Entity *, MessageOut &msg); + void sendTo(GameClient *, MessageOut &msg); /** * Kills connection with given character. */ - void kill(Character *); + void kill(Entity *); /** * Prepares a server change for given character. */ - void prepareServerChange(Character *); + void prepareServerChange(Entity *); /** * Completes a server change for given character ID. @@ -85,13 +87,13 @@ class GameHandler: public ConnectionHandler * Registers a character that should soon be claimed by a client. * @param token token used by the client when connecting. */ - void addPendingCharacter(const std::string &token, Character *); + void addPendingCharacter(const std::string &token, Entity *); /** * Combines a client with its character. * (Needed for TokenCollector) */ - void tokenMatched(GameClient *computer, Character *character); + void tokenMatched(GameClient *computer, Entity *character); /** * Deletes a pending client's data. @@ -103,13 +105,13 @@ class GameHandler: public ConnectionHandler * Deletes a pending connection's data. * (Needed for TokenCollector) */ - void deletePendingConnect(Character *character); + void deletePendingConnect(Entity *character); /** * Gets the character associated to a character name. This method is * slow, so it should never be called for regular operations. */ - Character *getCharacterByNameSlow(const std::string &) const; + Entity *getCharacterByNameSlow(const std::string &) const; protected: NetComputer *computerConnected(ENetPeer *); @@ -160,7 +162,7 @@ class GameHandler: public ConnectionHandler /** * Container for pending clients and pending connections. */ - TokenCollector<GameHandler, GameClient *, Character *> mTokenCollector; + TokenCollector<GameHandler, GameClient *, Entity *> mTokenCollector; }; extern GameHandler *gameHandler; diff --git a/src/game-server/inventory.cpp b/src/game-server/inventory.cpp index d523c7b8..e33e94a1 100644 --- a/src/game-server/inventory.cpp +++ b/src/game-server/inventory.cpp @@ -29,8 +29,15 @@ #include "net/messageout.h" #include "utils/logger.h" -Inventory::Inventory(Character *p): - mPoss(&p->getPossessions()), mCharacter(p) +Inventory::Inventory(Entity *p): + mPoss(&p->getComponent<CharacterComponent>()->getPossessions()), + mCharacter(p) +{ +} + +Inventory::Inventory(Entity *p, Possessions &possessions): + mPoss(&possessions), + mCharacter(p) { } @@ -90,7 +97,7 @@ void Inventory::initialize() { LOG_WARN("Inventory: deleting unknown item type " << it1->second.itemId << " from the inventory of '" - << mCharacter->getName() + << mCharacter->getComponent<BeingComponent>()->getName() << "'!"); mPoss->inventory.erase(it1++); } @@ -117,7 +124,7 @@ void Inventory::initialize() { LOG_WARN("Equipment: deleting unknown item id " << it2->second.itemId << " from the equipment of '" - << mCharacter->getName() + << mCharacter->getComponent<BeingComponent>()->getName() << "'!"); mPoss->equipSlots.erase(it2++); continue; @@ -157,7 +164,8 @@ unsigned Inventory::insert(unsigned itemId, unsigned amount) unsigned maxPerSlot = item->getMaxPerSlot(); LOG_DEBUG("Inventory: Inserting " << amount << " item(s) Id: " << itemId - << " for character '" << mCharacter->getName() << "'."); + << " for character '" + << mCharacter->getComponent<BeingComponent>()->getName() << "'."); InventoryData::iterator it, it_end = mPoss->inventory.end(); // Add to slots with existing items of this type first. @@ -278,7 +286,8 @@ unsigned Inventory::remove(unsigned itemId, unsigned amount) return amount; LOG_DEBUG("Inventory: Request remove of " << amount << " item(s) id: " - << itemId << " for character: '" << mCharacter->getName() + << itemId << " for character: '" + << mCharacter->getComponent<BeingComponent>()->getName() << "'."); MessageOut invMsg(GPMSG_INVENTORY); @@ -338,7 +347,8 @@ unsigned Inventory::move(unsigned slot1, unsigned slot2, unsigned amount) { LOG_DEBUG(amount << " item(s) requested to move from: " << slot1 << " to " - << slot2 << " for character: '" << mCharacter->getName() << "'."); + << slot2 << " for character: '" + << mCharacter->getComponent<BeingComponent>()->getName() << "'."); if (!amount || slot1 == slot2 || slot2 >= INVENTORY_SLOTS) return amount; @@ -467,7 +477,8 @@ unsigned Inventory::removeFromSlot(unsigned slot, unsigned amount) return amount; LOG_DEBUG("Inventory: Request Removal of " << amount << " item(s) in slot: " - << slot << " for character: '" << mCharacter->getName() << "'."); + << slot << " for character: '" + << mCharacter->getComponent<BeingComponent>()->getName() << "'."); MessageOut invMsg(GPMSG_INVENTORY); // Check if an item of the same id exists elsewhere in the inventory @@ -666,7 +677,8 @@ bool Inventory::equip(int inventorySlot) { // Something went wrong even when we tested the unequipment process. LOG_WARN("Unable to unequip even when unequip was tested. " - "Character : " << mCharacter->getName() + "Character : " + << mCharacter->getComponent<BeingComponent>()->getName() << ", unequip slot: " << *it3); return false; } @@ -722,8 +734,7 @@ bool Inventory::equip(int inventorySlot) { EquipmentItem equipItem(it->second.itemId, itemInstance); mPoss->equipSlots.insert( - std::make_pair<unsigned, EquipmentItem> - (equipReq.equipSlotId, equipItem)); + std::make_pair(equipReq.equipSlotId, equipItem)); --capacityLeft; } } @@ -839,5 +850,6 @@ bool Inventory::unequip(unsigned itemInstance) void Inventory::checkLookchanges(unsigned slotTypeId) { if (itemManager->isEquipSlotVisible(slotTypeId)) - mCharacter->raiseUpdateFlags(UPDATEFLAG_LOOKSCHANGE); + mCharacter->getComponent<ActorComponent>()->raiseUpdateFlags( + UPDATEFLAG_LOOKSCHANGE); } diff --git a/src/game-server/inventory.h b/src/game-server/inventory.h index 42856bff..1740a8d9 100644 --- a/src/game-server/inventory.h +++ b/src/game-server/inventory.h @@ -36,7 +36,8 @@ class Inventory /** * Creates a view on the possessions of a character. */ - Inventory(Character *); + explicit Inventory(Entity *); + Inventory(Entity *, Possessions &possessions); /** * Commits delayed changes if applicable. @@ -189,7 +190,7 @@ class Inventory Possessions *mPoss; /**< Pointer to the modified possessions. */ - Character *mCharacter; /**< Character to notify. */ + Entity *mCharacter; /**< Character to notify. */ }; #endif diff --git a/src/game-server/item.cpp b/src/game-server/item.cpp index c6c9d11c..444576f2 100644 --- a/src/game-server/item.cpp +++ b/src/game-server/item.cpp @@ -1,6 +1,7 @@ /* * 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. * @@ -18,51 +19,56 @@ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. */ - -#include <string> -#include <map> - #include "game-server/item.h" #include "common/configuration.h" #include "game-server/attack.h" #include "game-server/attributemanager.h" #include "game-server/being.h" +#include "game-server/combatcomponent.h" #include "game-server/state.h" #include "scripting/script.h" #include "scripting/scriptmanager.h" -bool ItemEffectAttrMod::apply(Being *itemUser) +#include <map> +#include <string> + +bool ItemEffectAttrMod::apply(Entity *itemUser) { LOG_DEBUG("Applying modifier."); - itemUser->applyModifier(mAttributeId, mMod, mAttributeLayer, - mDuration, mId); + itemUser->getComponent<BeingComponent>()->applyModifier(*itemUser, + mAttributeId, mMod, + mAttributeLayer, + mDuration, mId); return false; } -void ItemEffectAttrMod::dispell(Being *itemUser) +void ItemEffectAttrMod::dispell(Entity *itemUser) { LOG_DEBUG("Dispelling modifier."); - itemUser->removeModifier(mAttributeId, mMod, mAttributeLayer, - mId, !mDuration); + itemUser->getComponent<BeingComponent>()->removeModifier(*itemUser, + mAttributeId, + mMod, + mAttributeLayer, + mId, !mDuration); } -bool ItemEffectAttack::apply(Being *itemUser) +bool ItemEffectAttack::apply(Entity *itemUser) { - itemUser->addAttack(mAttackInfo); + itemUser->getComponent<CombatComponent>()->addAttack(mAttackInfo); return false; } -void ItemEffectAttack::dispell(Being *itemUser) +void ItemEffectAttack::dispell(Entity *itemUser) { - itemUser->removeAttack(mAttackInfo); + itemUser->getComponent<CombatComponent>()->removeAttack(mAttackInfo); } ItemEffectScript::~ItemEffectScript() { } -bool ItemEffectScript::apply(Being *itemUser) +bool ItemEffectScript::apply(Entity *itemUser) { if (mActivateEventName.empty()) return false; @@ -81,7 +87,7 @@ bool ItemEffectScript::apply(Being *itemUser) return false; } -void ItemEffectScript::dispell(Being *itemUser) +void ItemEffectScript::dispell(Entity *itemUser) { if (mDispellEventName.empty()) return; @@ -122,7 +128,7 @@ void ItemClass::addEffect(ItemEffectInfo *effect, mDispells.insert(std::make_pair(dispell, effect)); } -bool ItemClass::useTrigger(Being *itemUser, ItemTriggerType trigger) +bool ItemClass::useTrigger(Entity *itemUser, ItemTriggerType trigger) { if (!trigger) return false; @@ -150,19 +156,37 @@ void ItemClass::addAttack(AttackInfo *attackInfo, addEffect(new ItemEffectAttack(attackInfo), applyTrigger, dispellTrigger); } - -Item::Item(ItemClass *type, int amount) - : Actor(OBJECT_ITEM), mType(type), mAmount(amount) +ItemComponent::ItemComponent(ItemClass *type, int amount) : + mType(type), + mAmount(amount) { mLifetime = Configuration::getValue("game_floorItemDecayTime", 0) * 10; } -void Item::update() +void ItemComponent::update(Entity &entity) { if (mLifetime) { mLifetime--; if (!mLifetime) - GameState::enqueueRemove(this); + GameState::enqueueRemove(&entity); } } + +namespace Item { + +Entity *create(MapComposite *map, + Point pos, + ItemClass *itemClass, + int amount) +{ + Entity *itemActor = new Entity(OBJECT_ITEM); + ActorComponent *actorComponent = new ActorComponent(*itemActor); + itemActor->addComponent(actorComponent); + itemActor->addComponent(new ItemComponent(itemClass, amount)); + itemActor->setMap(map); + actorComponent->setPosition(*itemActor, pos); + return itemActor; +} + +} // namespace Item diff --git a/src/game-server/item.h b/src/game-server/item.h index 54112c4a..76242094 100644 --- a/src/game-server/item.h +++ b/src/game-server/item.h @@ -1,6 +1,7 @@ /* * 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. * @@ -27,8 +28,9 @@ #include "game-server/attack.h" #include "scripting/script.h" -class Being; +class Entity; class ItemClass; +class MapComposite; // Indicates the equip slot "cost" to equip an item. struct ItemEquipRequirement { @@ -103,8 +105,8 @@ class ItemEffectInfo public: virtual ~ItemEffectInfo() {} - virtual bool apply(Being *itemUser) = 0; - virtual void dispell(Being *itemUser) = 0; + virtual bool apply(Entity *itemUser) = 0; + virtual void dispell(Entity *itemUser) = 0; }; class ItemEffectAttrMod : public ItemEffectInfo @@ -116,8 +118,8 @@ class ItemEffectAttrMod : public ItemEffectInfo mMod(value), mDuration(duration), mId(id) {} - bool apply(Being *itemUser); - void dispell(Being *itemUser); + bool apply(Entity *itemUser); + void dispell(Entity *itemUser); private: unsigned mAttributeId; @@ -134,8 +136,8 @@ class ItemEffectAttack : public ItemEffectInfo mAttackInfo(attackInfo) {} - bool apply(Being *itemUser); - void dispell(Being *itemUser); + bool apply(Entity *itemUser); + void dispell(Entity *itemUser); private: AttackInfo *mAttackInfo; }; @@ -143,9 +145,9 @@ class ItemEffectAttack : public ItemEffectInfo class ItemEffectConsumes : public ItemEffectInfo { public: - bool apply(Being *) + bool apply(Entity *) { return true; } - void dispell(Being *) + void dispell(Entity *) {} }; @@ -162,8 +164,8 @@ class ItemEffectScript : public ItemEffectInfo ~ItemEffectScript(); - bool apply(Being *itemUser); - void dispell(Being *itemUser); + bool apply(Entity *itemUser); + void dispell(Entity *itemUser); private: ItemClass *mItemClass; @@ -204,7 +206,7 @@ class ItemClass * Applies the modifiers of an item to a given user. * @return true if item should be removed. */ - bool useTrigger(Being *itemUser, ItemTriggerType trigger); + bool useTrigger(Entity *itemUser, ItemTriggerType trigger); /** * Gets unit cost of these items. @@ -294,12 +296,14 @@ class ItemClass }; /** - * Class for an item stack laying on the floor in the game world + * An item stack lying on the floor in the game world. */ -class Item : public Actor +class ItemComponent : public Component { public: - Item(ItemClass *type, int amount); + static const ComponentType type = CT_Item; + + ItemComponent(ItemClass *type, int amount); ItemClass *getItemClass() const { return mType; } @@ -307,7 +311,7 @@ class Item : public Actor int getAmount() const { return mAmount; } - virtual void update(); + void update(Entity &entity); private: ItemClass *mType; @@ -315,4 +319,23 @@ class Item : public Actor int mLifetime; }; +namespace Item { + +/** + * @brief Creates an item actor. + * + * The returned actor should be inserted into the game state, usually with + * either GameState::insertOrDelete or GameState::enqueueInsert. + * + * @param map the map of the item + * @param pos the position of the item + * @param itemClass the class of the item + * @param amount the amount of items on the stack + * + * @return the created item + */ +Entity *create(MapComposite *map, Point pos, ItemClass *itemClass, int amount); + +} // namespace Item + #endif // ITEM_H diff --git a/src/game-server/itemmanager.cpp b/src/game-server/itemmanager.cpp index 4837ce98..504ba127 100644 --- a/src/game-server/itemmanager.cpp +++ b/src/game-server/itemmanager.cpp @@ -159,8 +159,7 @@ void ItemManager::readEquipSlotsFile() << ", capacity: " << capacity << ", visible? " << visible); EquipSlotInfo *equipSlotInfo = new EquipSlotInfo(slotId, name, capacity, visible); - mEquipSlotsInfo.insert( - std::make_pair<unsigned, EquipSlotInfo*>(slotId, equipSlotInfo)); + mEquipSlotsInfo.insert(std::make_pair(slotId, equipSlotInfo)); mNamedEquipSlotsInfo.insert(name, equipSlotInfo); totalCapacity += capacity; diff --git a/src/game-server/main-game.cpp b/src/game-server/main-game.cpp index 2533a16b..3a12cdb8 100644 --- a/src/game-server/main-game.cpp +++ b/src/game-server/main-game.cpp @@ -1,7 +1,7 @@ /* * The Mana Server * Copyright (C) 2004-2010 The Mana World Development Team - * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2010-2012 The Mana Developers * * This file is part of The Mana Server. * diff --git a/src/game-server/mapcomposite.cpp b/src/game-server/mapcomposite.cpp index 99d2734f..ec881cfa 100644 --- a/src/game-server/mapcomposite.cpp +++ b/src/game-server/mapcomposite.cpp @@ -1,7 +1,7 @@ /* * The Mana Server * Copyright (C) 2006-2010 The Mana World Development Team - * Copyright (C) 2010-2011 The Mana Development Team + * Copyright (C) 2010-2012 The Mana Development Team * * This file is part of The Mana Server. * @@ -26,13 +26,14 @@ #include "common/configuration.h" #include "common/resourcemanager.h" #include "game-server/character.h" +#include "game-server/combatcomponent.h" #include "game-server/mapcomposite.h" #include "game-server/map.h" #include "game-server/mapmanager.h" #include "game-server/mapreader.h" #include "game-server/monstermanager.h" -#include "game-server/spawnarea.h" -#include "game-server/trigger.h" +#include "game-server/spawnareacomponent.h" +#include "game-server/triggerareacomponent.h" #include "scripting/script.h" #include "scripting/scriptmanager.h" #include "utils/logger.h" @@ -52,7 +53,7 @@ in dealing with zone changes. */ static int const zoneDiam = 256; -void MapZone::insert(Actor *obj) +void MapZone::insert(Entity *obj) { int type = obj->getType(); switch (type) @@ -97,9 +98,9 @@ void MapZone::insert(Actor *obj) } } -void MapZone::remove(Actor *obj) +void MapZone::remove(Entity *obj) { - std::vector< Actor * >::iterator i_beg = objects.begin(), i, i_end; + std::vector< Entity * >::iterator i_beg = objects.begin(), i, i_end; int type = obj->getType(); switch (type) { @@ -181,7 +182,7 @@ CharacterIterator::CharacterIterator(const ZoneIterator &it) while (iterator && (*iterator)->nbCharacters == 0) ++iterator; if (iterator) { - current = static_cast< Character * >((*iterator)->objects[pos]); + current = (*iterator)->objects[pos]; } } @@ -194,7 +195,7 @@ void CharacterIterator::operator++() } if (iterator) { - current = static_cast< Character * >((*iterator)->objects[pos]); + current = (*iterator)->objects[pos]; } } @@ -204,7 +205,7 @@ BeingIterator::BeingIterator(const ZoneIterator &it) while (iterator && (*iterator)->nbMovingObjects == 0) ++iterator; if (iterator) { - current = static_cast< Being * >((*iterator)->objects[pos]); + current = (*iterator)->objects[pos]; } } @@ -217,7 +218,7 @@ void BeingIterator::operator++() } if (iterator) { - current = static_cast< Being * >((*iterator)->objects[pos]); + current = (*iterator)->objects[pos]; } } @@ -371,15 +372,18 @@ MapContent::~MapContent() delete[] zones; } -bool MapContent::allocate(Actor *obj) +bool MapContent::allocate(Entity *obj) { // First, try allocating from the last used bucket. ObjectBucket *b = buckets[last_bucket]; + + auto *actorComponent = obj->getComponent<ActorComponent>(); + int i = b->allocate(); if (i >= 0) { b->objects[i] = obj; - obj->setPublicID(last_bucket * 256 + i); + actorComponent->setPublicID(last_bucket * 256 + i); return true; } @@ -402,7 +406,7 @@ bool MapContent::allocate(Actor *obj) { last_bucket = i; b->objects[j] = obj; - obj->setPublicID(last_bucket * 256 + j); + actorComponent->setPublicID(last_bucket * 256 + j); return true; } } @@ -412,9 +416,9 @@ bool MapContent::allocate(Actor *obj) return false; } -void MapContent::deallocate(Actor *obj) +void MapContent::deallocate(Entity *obj) { - unsigned short id = obj->getPublicID(); + unsigned short id = obj->getComponent<ActorComponent>()->getPublicID(); buckets[id / 256]->deallocate(id % 256); } @@ -526,10 +530,11 @@ ZoneIterator MapComposite::getAroundPointIterator(const Point &p, int radius) co return ZoneIterator(r, mContent); } -ZoneIterator MapComposite::getAroundActorIterator(Actor *obj, int radius) const +ZoneIterator MapComposite::getAroundActorIterator(Entity *obj, int radius) const { MapRegion r; - mContent->fillRegion(r, obj->getPosition(), radius); + mContent->fillRegion(r, obj->getComponent<ActorComponent>()->getPosition(), + radius); return ZoneIterator(r, mContent); } @@ -540,10 +545,12 @@ ZoneIterator MapComposite::getInsideRectangleIterator(const Rectangle &p) const return ZoneIterator(r, mContent); } -ZoneIterator MapComposite::getAroundBeingIterator(Being *obj, int radius) const +ZoneIterator MapComposite::getAroundBeingIterator(Entity *obj, int radius) const { MapRegion r1; - mContent->fillRegion(r1, obj->getOldPosition(), radius); + mContent->fillRegion(r1, + obj->getComponent<BeingComponent>()->getOldPosition(), + radius); MapRegion r2 = r1; for (MapRegion::iterator i = r1.begin(), i_end = r1.end(); i != i_end; ++i) { @@ -561,7 +568,9 @@ ZoneIterator MapComposite::getAroundBeingIterator(Being *obj, int radius) const r2.swap(r3); } } - mContent->fillRegion(r2, obj->getPosition(), radius); + mContent->fillRegion(r2, + obj->getComponent<ActorComponent>()->getPosition(), + radius); return ZoneIterator(r2, mContent); } @@ -569,13 +578,12 @@ bool MapComposite::insert(Entity *ptr) { if (ptr->isVisible()) { - if (ptr->canMove() && !mContent->allocate(static_cast< Being * >(ptr))) - { + if (ptr->canMove() && !mContent->allocate(ptr)) return false; - } - Actor *obj = static_cast< Actor * >(ptr); - mContent->getZone(obj->getPosition()).insert(obj); + const Point &point = + ptr->getComponent<ActorComponent>()->getPosition(); + mContent->getZone(point).insert(ptr); } ptr->setMap(this); @@ -590,10 +598,9 @@ void MapComposite::remove(Entity *ptr) { if ((*i)->canFight()) { - Being *being = static_cast<Being*>(*i); - if (being->getTarget() == ptr) + if ((*i)->getComponent<CombatComponent>()->getTarget() == ptr) { - being->setTarget(NULL); + (*i)->getComponent<CombatComponent>()->clearTarget(); } } if (*i == ptr) @@ -604,12 +611,13 @@ void MapComposite::remove(Entity *ptr) if (ptr->isVisible()) { - Actor *obj = static_cast< Actor * >(ptr); - mContent->getZone(obj->getPosition()).remove(obj); + const Point &point = + ptr->getComponent<ActorComponent>()->getPosition(); + mContent->getZone(point).remove(ptr); if (ptr->canMove()) { - mContent->deallocate(static_cast< Being * >(ptr)); + mContent->deallocate(ptr); } } } @@ -635,7 +643,7 @@ void MapComposite::update() // Move objects around and update zones. for (BeingIterator it(getWholeMapIterator()); it; ++it) { - (*it)->move(); + (*it)->getComponent<BeingComponent>()->move(**it); } for (int i = 0; i < mContent->mapHeight * mContent->mapWidth; ++i) @@ -650,18 +658,18 @@ void MapComposite::update() if (!(*i)->canMove()) continue; - Being *obj = static_cast< Being * >(*i); - - const Point &pos1 = obj->getOldPosition(), - &pos2 = obj->getPosition(); + const Point &pos1 = + (*i)->getComponent<BeingComponent>()->getOldPosition(); + const Point &pos2 = + (*i)->getComponent<ActorComponent>()->getPosition(); MapZone &src = mContent->getZone(pos1), &dst = mContent->getZone(pos2); if (&src != &dst) { addZone(src.destinations, &dst - mContent->zones); - src.remove(obj); - dst.insert(obj); + src.remove(*i); + dst.insert(*i); } } } @@ -800,9 +808,13 @@ void MapComposite::initializeContent() if (destMap && destX && destY) { - WarpAction *action = new WarpAction(destMap, destX, destY); - insert(new TriggerArea(this, object->getBounds(), - action, false)); + Entity *entity = new Entity(OBJECT_OTHER, this); + const Point warpTarget(destX, destY); + WarpAction *action = new WarpAction(destMap, warpTarget); + entity->addComponent( + new TriggerAreaComponent(object->getBounds(), + action, false)); + insert(entity); } else { @@ -838,8 +850,13 @@ void MapComposite::initializeContent() if (monster && maxBeings && spawnRate) { - insert(new SpawnArea(this, monster, object->getBounds(), - maxBeings, spawnRate)); + Entity *entity = new Entity(OBJECT_OTHER, this); + SpawnAreaComponent *spawnArea = + new SpawnAreaComponent(monster, object->getBounds(), + maxBeings, spawnRate); + + entity->addComponent(spawnArea); + insert(entity); } } else if (utils::compareStrI(type, "NPC") == 0) diff --git a/src/game-server/mapcomposite.h b/src/game-server/mapcomposite.h index ba76ddc8..219f2d9f 100644 --- a/src/game-server/mapcomposite.h +++ b/src/game-server/mapcomposite.h @@ -29,14 +29,12 @@ #include "scripting/script.h" #include "game-server/map.h" -class Actor; -class Being; -class Character; +class CharacterComponent; +class Entity; class Map; class MapComposite; class Point; class Rectangle; -class Entity; struct MapContent; struct MapZone; @@ -76,11 +74,11 @@ struct CharacterIterator { ZoneIterator iterator; unsigned short pos; - Character *current; + Entity *current; CharacterIterator(const ZoneIterator &); void operator++(); - Character *operator*() const { return current; } + Entity *operator*() const { return current; } operator bool() const { return iterator; } }; @@ -91,11 +89,11 @@ struct BeingIterator { ZoneIterator iterator; unsigned short pos; - Being *current; + Entity *current; BeingIterator(const ZoneIterator &); void operator++(); - Being *operator*() const { return current; } + Entity *operator*() const { return current; } operator bool() const { return iterator; } }; @@ -106,11 +104,11 @@ struct FixedActorIterator { ZoneIterator iterator; unsigned short pos; - Actor *current; + Entity *current; FixedActorIterator(const ZoneIterator &); void operator++(); - Actor *operator*() const { return current; } + Entity *operator*() const { return current; } operator bool() const { return iterator; } }; @@ -121,11 +119,11 @@ struct ActorIterator { ZoneIterator iterator; unsigned short pos; - Actor *current; + Entity *current; ActorIterator(const ZoneIterator &); void operator++(); - Actor *operator*() const { return current; } + Entity *operator*() const { return current; } operator bool() const { return iterator; } }; @@ -140,7 +138,7 @@ struct MapZone * Characters are stored first, then the remaining MovingObjects, then the * remaining Objects. */ - std::vector< Actor * > objects; + std::vector< Entity * > objects; /** * Destinations of the objects that left this zone. @@ -150,8 +148,8 @@ struct MapZone MapRegion destinations; MapZone(): nbCharacters(0), nbMovingObjects(0) {} - void insert(Actor *); - void remove(Actor *); + void insert(Entity *); + void remove(Entity *); }; /** @@ -165,7 +163,7 @@ struct ObjectBucket unsigned bitmap[256 / int_bitsize]; /**< Bitmap of free locations. */ short free; /**< Number of empty places. */ short next_object; /**< Next object to look at. */ - Actor *objects[256]; + Entity *objects[256]; ObjectBucket(); int allocate(); @@ -183,12 +181,12 @@ struct MapContent /** * Allocates a unique ID for an actor on this map. */ - bool allocate(Actor *); + bool allocate(Entity *); /** * Deallocates an ID. */ - void deallocate(Actor *); + void deallocate(Entity *); /** * Fills a region of zones within the range of a point. @@ -309,13 +307,13 @@ class MapComposite /** * Gets an iterator on the objects around a given actor. */ - ZoneIterator getAroundActorIterator(Actor *, int radius) const; + ZoneIterator getAroundActorIterator(Entity *, int radius) const; /** * Gets an iterator on the objects around the old and new positions of * a character (including the ones that were but are now elsewhere). */ - ZoneIterator getAroundBeingIterator(Being *, int radius) const; + ZoneIterator getAroundBeingIterator(Entity *, int radius) const; /** * Gets everything related to the map. diff --git a/src/game-server/monster.cpp b/src/game-server/monster.cpp index 3899c792..be0e1618 100644 --- a/src/game-server/monster.cpp +++ b/src/game-server/monster.cpp @@ -28,6 +28,7 @@ #include "game-server/item.h" #include "game-server/map.h" #include "game-server/mapcomposite.h" +#include "game-server/monstercombatcomponent.h" #include "game-server/state.h" #include "scripting/scriptmanager.h" #include "utils/logger.h" @@ -52,26 +53,27 @@ double MonsterClass::getVulnerability(Element element) const return it->second; } -Monster::Monster(MonsterClass *specy): - Being(OBJECT_MONSTER), +MonsterComponent::MonsterComponent(Entity &entity, MonsterClass *specy): mSpecy(specy), - mOwner(NULL) + mOwner(nullptr) { LOG_DEBUG("Monster spawned! (id: " << mSpecy->getId() << ")."); - setWalkMask(Map::BLOCKMASK_WALL | Map::BLOCKMASK_CHARACTER); + auto *beingComponent = entity.getComponent<BeingComponent>(); + + auto *actorComponent = entity.getComponent<ActorComponent>(); + actorComponent->setWalkMask(Map::BLOCKMASK_WALL | + Map::BLOCKMASK_CHARACTER); + actorComponent->setBlockType(BLOCKTYPE_MONSTER); + actorComponent->setSize(specy->getSize()); /* * Initialise the attribute structures. */ - const AttributeManager::AttributeScope &mobAttr = - attributeManager->getAttributeScope(MonsterScope); - for (AttributeManager::AttributeScope::const_iterator it = mobAttr.begin(), - it_end = mobAttr.end(); it != it_end; ++it) + for (auto attrInfo : attributeManager->getAttributeScope(MonsterScope)) { - mAttributes.insert(std::pair< unsigned, Attribute > - (it->first, Attribute(*it->second))); + beingComponent->createAttribute(attrInfo.first, *attrInfo.second); } /* @@ -81,27 +83,21 @@ Monster::Monster(MonsterClass *specy): int mutation = specy->getMutation(); - for (AttributeMap::iterator it2 = mAttributes.begin(), - it2_end = mAttributes.end(); it2 != it2_end; ++it2) + for (auto &attribute : specy->getAttributes()) { - double attr = 0.0f; - - if (specy->hasAttribute(it2->first)) + double attributeValue = attribute.second; + if (mutation != 0) { - attr = specy->getAttribute(it2->first); - - setAttribute(it2->first, - mutation ? - attr * (100 + (rand() % (mutation * 2)) - mutation) / 100.0 : - attr); + double factor = 100 + (rand() % (mutation * 2)) - mutation; + attributeValue = attributeValue * factor / 100.0; } + beingComponent->setAttribute(entity, attribute.first, attributeValue); } - mDamageMutation = mutation ? - (100 + (rand() % (mutation * 2)) - mutation) / 100.0 : 1; + beingComponent->setGender(specy->getGender()); - setSize(specy->getSize()); - setGender(specy->getGender()); + beingComponent->signal_died.connect(sigc::mem_fun(this, + &MonsterComponent::monsterDied)); // Set positions relative to target from which the monster can attack int dist = specy->getAttackDistance(); @@ -110,31 +106,34 @@ Monster::Monster(MonsterClass *specy): mAttackPositions.push_back(AttackPosition(0, -dist, DOWN)); mAttackPositions.push_back(AttackPosition(0, dist, UP)); - // Take attacks from specy - std::vector<AttackInfo *> &attacks = specy->getAttackInfos(); - for (std::vector<AttackInfo *>::iterator it = attacks.begin(), - it_end = attacks.end(); it != it_end; ++it) - { - addAttack(*it); - } + MonsterCombatComponent *combatComponent = + new MonsterCombatComponent(entity, specy); + entity.addComponent(combatComponent); + + double damageMutation = mutation ? + (100.0 + (rand() % (mutation * 2)) - mutation) / 100.0 : 1.0; + combatComponent->setDamageMutation(damageMutation); + + combatComponent->signal_damaged.connect( + sigc::mem_fun(this, &MonsterComponent::receivedDamage)); } -Monster::~Monster() +MonsterComponent::~MonsterComponent() { } -void Monster::update() +void MonsterComponent::update(Entity &entity) { - Being::update(); - if (mKillStealProtectedTimeout.justFinished()) mOwner = NULL; + auto *beingComponent = entity.getComponent<BeingComponent>(); + // If dead, remove it - if (mAction == DEAD) + if (beingComponent->getAction() == DEAD) { if (mDecayTimeout.expired()) - GameState::enqueueRemove(this); + GameState::enqueueRemove(&entity); return; } @@ -143,70 +142,76 @@ void Monster::update() { Script *script = ScriptManager::currentState(); script->prepare(mSpecy->getUpdateCallback()); - script->push(this); - script->execute(getMap()); + script->push(&entity); + script->execute(entity.getMap()); } - refreshTarget(); + refreshTarget(entity); // Cancel the rest when we have a target - if (mTarget) + if (entity.getComponent<CombatComponent>()->getTarget()) return; + const Point &position = + entity.getComponent<ActorComponent>()->getPosition(); + // We have no target - let's wander around - if (mStrollTimeout.expired() && getPosition() == getDestination()) + if (mStrollTimeout.expired() && + position == beingComponent->getDestination()) { if (mKillStealProtectedTimeout.expired()) { unsigned range = mSpecy->getStrollRange(); if (range) { - Point randomPos(rand() % (range * 2 + 1) - - range + getPosition().x, - rand() % (range * 2 + 1) - - range + getPosition().y); + Point randomPos(rand() % (range * 2 + 1) - range + position.x, + rand() % (range * 2 + 1) - range + position.y); // Don't allow negative destinations, to avoid rounding // problems when divided by tile size if (randomPos.x >= 0 && randomPos.y >= 0) - setDestination(randomPos); + beingComponent->setDestination(entity, randomPos); } mStrollTimeout.set(10 + rand() % 10); } } } -void Monster::refreshTarget() +void MonsterComponent::refreshTarget(Entity &entity) { + auto *beingComponent = entity.getComponent<BeingComponent>(); + // We are dead and sadly not possible to keep attacking :( - if (mAction == DEAD) + if (beingComponent->getAction() == DEAD) return; // Check potential attack positions int bestTargetPriority = 0; - Being *bestTarget = 0; + Entity *bestTarget = 0; Point bestAttackPosition; // reset Target. We will find a new one if possible - mTarget = 0; + entity.getComponent<CombatComponent>()->clearTarget(); // Iterate through objects nearby int aroundArea = Configuration::getValue("game_visualRange", 448); - for (BeingIterator i(getMap()->getAroundBeingIterator(this, aroundArea)); + for (BeingIterator i(entity.getMap()->getAroundBeingIterator(&entity, + aroundArea)); i; ++i) { // We only want to attack player characters if ((*i)->getType() != OBJECT_CHARACTER) continue; - Being *target = static_cast<Being *>(*i); + Entity *target = *i; // Dead characters are ignored - if (target->getAction() == DEAD) + if (beingComponent->getAction() == DEAD) continue; // Determine how much we hate the target int targetPriority = 0; - std::map<Being *, AggressionInfo>::iterator angerIterator = mAnger.find(target); + std::map<Entity *, AggressionInfo>::iterator angerIterator = + mAnger.find(target); if (angerIterator != mAnger.end()) { const AggressionInfo &aggressionInfo = angerIterator->second; @@ -225,11 +230,13 @@ void Monster::refreshTarget() for (std::list<AttackPosition>::iterator j = mAttackPositions.begin(); j != mAttackPositions.end(); j++) { - Point attackPosition = target->getPosition(); + Point attackPosition = + target->getComponent<ActorComponent>()->getPosition(); attackPosition.x += j->x; attackPosition.y += j->y; - int posPriority = calculatePositionPriority(attackPosition, + int posPriority = calculatePositionPriority(entity, + attackPosition, targetPriority); if (posPriority > bestTargetPriority) { @@ -241,55 +248,34 @@ void Monster::refreshTarget() } if (bestTarget) { - mTarget = bestTarget; - if (bestAttackPosition == getPosition()) + const Point &ownPosition = + entity.getComponent<ActorComponent>()->getPosition(); + const Point &targetPosition = + bestTarget->getComponent<ActorComponent>()->getPosition(); + + entity.getComponent<CombatComponent>()->setTarget(bestTarget); + if (bestAttackPosition == ownPosition) { - mAction = ATTACK; - updateDirection(getPosition(), mTarget->getPosition()); + beingComponent->setAction(entity, ATTACK); + beingComponent->updateDirection(entity, ownPosition, + targetPosition); } else { - setDestination(bestAttackPosition); + beingComponent->setDestination(entity, bestAttackPosition); } } } -void Monster::processAttack(Attack &attack) -{ - if (!mTarget) - { - setAction(STAND); - return; - } - - Damage dmg = attack.getAttackInfo()->getDamage(); - dmg.skill = 0; - dmg.base *= mDamageMutation; - dmg.delta *= mDamageMutation; - - int hit = performAttack(mTarget, attack.getAttackInfo()->getDamage()); - - const Script::Ref &scriptCallback = - attack.getAttackInfo()->getScriptCallback(); - - if (scriptCallback.isValid() && hit > -1) - { - Script *script = ScriptManager::currentState(); - script->prepare(scriptCallback); - script->push(this); - script->push(mTarget); - script->push(hit); - script->execute(getMap()); - } -} - -int Monster::calculatePositionPriority(Point position, int targetPriority) +int MonsterComponent::calculatePositionPriority(Entity &entity, + Point position, + int targetPriority) { - Point thisPos = getPosition(); + Point thisPos = entity.getComponent<ActorComponent>()->getPosition(); unsigned range = mSpecy->getTrackRange(); - Map *map = getMap()->getMap(); + Map *map = entity.getMap()->getMap(); int tileWidth = map->getTileWidth(); int tileHeight = map->getTileHeight(); @@ -303,7 +289,7 @@ int Monster::calculatePositionPriority(Point position, int targetPriority) Path path; path = map->findPath(thisPos.x / tileWidth, thisPos.y / tileHeight, position.x / tileWidth, position.y / tileHeight, - getWalkMask(), + entity.getComponent<ActorComponent>()->getWalkMask(), range); if (path.empty() || path.size() >= range) @@ -316,54 +302,51 @@ int Monster::calculatePositionPriority(Point position, int targetPriority) } } -void Monster::forgetTarget(Entity *entity) +void MonsterComponent::forgetTarget(Entity *entity) { - Being *b = static_cast< Being * >(entity); { - AggressionInfo &aggressionInfo = mAnger[b]; + AggressionInfo &aggressionInfo = mAnger[entity]; aggressionInfo.removedConnection.disconnect(); aggressionInfo.diedConnection.disconnect(); } - mAnger.erase(b); + mAnger.erase(entity); - if (b->getType() == OBJECT_CHARACTER) + if (entity->getType() == OBJECT_CHARACTER) { - Character *c = static_cast< Character * >(b); - mExpReceivers.erase(c); - mLegalExpReceivers.erase(c); + mExpReceivers.erase(entity); + mLegalExpReceivers.erase(entity); } } -void Monster::changeAnger(Actor *target, int amount) +void MonsterComponent::changeAnger(Entity *target, int amount) { const EntityType type = target->getType(); if (type != OBJECT_MONSTER && type != OBJECT_CHARACTER) return; - Being *being = static_cast< Being * >(target); - - if (mAnger.find(being) != mAnger.end()) + if (mAnger.find(target) != mAnger.end()) { - mAnger[being].anger += amount; + mAnger[target].anger += amount; } else { - AggressionInfo &aggressionInfo = mAnger[being]; + AggressionInfo &aggressionInfo = mAnger[target]; aggressionInfo.anger = amount; // Forget target either when it's removed or died, whichever // happens first. aggressionInfo.removedConnection = - being->signal_removed.connect(sigc::mem_fun(this, &Monster::forgetTarget)); - aggressionInfo.diedConnection = - being->signal_died.connect(sigc::mem_fun(this, &Monster::forgetTarget)); + target->signal_removed.connect(sigc::mem_fun(this, &MonsterComponent::forgetTarget)); + aggressionInfo.diedConnection = target->getComponent<BeingComponent>() + ->signal_died.connect( + sigc::mem_fun(this, &MonsterComponent::forgetTarget)); } } -std::map<Being *, int> Monster::getAngerList() const +std::map<Entity *, int> MonsterComponent::getAngerList() const { - std::map<Being *, int> result; - std::map<Being *, AggressionInfo>::const_iterator i, i_end; + std::map<Entity *, int> result; + std::map<Entity *, AggressionInfo>::const_iterator i, i_end; for (i = mAnger.begin(), i_end = mAnger.end(); i != i_end; ++i) { @@ -374,49 +357,8 @@ std::map<Being *, int> Monster::getAngerList() const return result; } -int Monster::damage(Actor *source, const Damage &damage) -{ - Damage newDamage = damage; - float factor = mSpecy->getVulnerability(newDamage.element); - newDamage.base = newDamage.base * factor; - newDamage.delta = newDamage.delta * factor; - int HPLoss = Being::damage(source, newDamage); - if (source) - changeAnger(source, HPLoss); - - if (HPLoss && source && source->getType() == OBJECT_CHARACTER) - { - Character *s = static_cast< Character * >(source); - - mExpReceivers[s].insert(damage.skill); - if (mKillStealProtectedTimeout.expired() || mOwner == s - || mOwner->getParty() == s->getParty()) - { - mOwner = s; - mLegalExpReceivers.insert(s); - mKillStealProtectedTimeout.set(KILLSTEAL_PROTECTION_TIME); - } - } - - if (mSpecy->getDamageCallback().isValid()) - { - Script *script = ScriptManager::currentState(); - script->prepare(mSpecy->getDamageCallback()); - script->push(this); - script->push(source); - script->push(HPLoss); - // TODO: add exact damage parameters as well - script->execute(getMap()); - } - - return HPLoss; -} - -void Monster::died() +void MonsterComponent::monsterDied(Entity *monster) { - if (mAction == DEAD) return; - - Being::died(); mDecayTimeout.set(DECAY_TIME); if (mExpReceivers.size() > 0) @@ -426,17 +368,20 @@ void Monster::died() for (unsigned i = 0; i < size; i++) { const int p = rand() / (RAND_MAX / 10000); - if (p <= mSpecy->mDrops[i].probability) + const MonsterDrop &drop = mSpecy->mDrops[i]; + + if (p <= drop.probability) { - Item *item = new Item(mSpecy->mDrops[i].item, 1); - item->setMap(getMap()); - item->setPosition(getPosition()); + const Point &position = + monster->getComponent<ActorComponent>()->getPosition(); + Entity *item = Item::create(monster->getMap(), position, + drop.item, 1); GameState::enqueueInsert(item); } } // Distribute exp reward. - std::map<Character *, std::set <size_t> > ::iterator iChar; + std::map<Entity *, std::set <size_t> > ::iterator iChar; std::set<size_t>::iterator iSkill; @@ -445,21 +390,44 @@ void Monster::died() for (iChar = mExpReceivers.begin(); iChar != mExpReceivers.end(); iChar++) { - Character *character = (*iChar).first; + auto *character = (*iChar).first; const std::set<size_t> &skillSet = (*iChar).second; if (mLegalExpReceivers.find(character) == mLegalExpReceivers.end() || skillSet.empty()) continue; + auto characterComponent = + character->getComponent<CharacterComponent>(); + int expPerSkill = int(expPerChar / skillSet.size()); for (iSkill = skillSet.begin(); iSkill != skillSet.end(); iSkill++) { - character->receiveExperience(*iSkill, expPerSkill, - mSpecy->getOptimalLevel()); + characterComponent->receiveExperience(*iSkill, expPerSkill, + mSpecy->getOptimalLevel()); } - character->incrementKillCount(mSpecy->getId()); + characterComponent->incrementKillCount(mSpecy->getId()); + } + } +} + + +void MonsterComponent::receivedDamage(Entity *source, const Damage &damage, int hpLoss) +{ + if (source) + changeAnger(source, hpLoss); + + if (hpLoss && source && source->getType() == OBJECT_CHARACTER) + { + mExpReceivers[source].insert(damage.skill); + if (mKillStealProtectedTimeout.expired() || mOwner == source + || mOwner->getComponent<CharacterComponent>()->getParty() == + source->getComponent<CharacterComponent>()->getParty()) + { + mOwner = source; + mLegalExpReceivers.insert(source); + mKillStealProtectedTimeout.set(KILLSTEAL_PROTECTION_TIME); } } } diff --git a/src/game-server/monster.h b/src/game-server/monster.h index 6bf06d2b..2e238201 100644 --- a/src/game-server/monster.h +++ b/src/game-server/monster.h @@ -27,12 +27,13 @@ #include "utils/string.h" #include <map> -#include <vector> +#include <set> #include <string> +#include <vector> #include <sigc++/connection.h> -class Character; +class CharacterComponent; class ItemClass; class Script; @@ -138,6 +139,9 @@ class MonsterClass bool hasAttribute(int attribute) const { return (mAttributes.find(attribute) != mAttributes.end()); } + const std::map<int, double> &getAttributes() const + { return mAttributes; } + /** Sets collision circle radius. */ void setSize(int size) { mSize = size; } @@ -245,7 +249,7 @@ class MonsterClass Script::Ref mDamageCallback; friend class MonsterManager; - friend class Monster; + friend class MonsterComponent; }; /** @@ -266,16 +270,18 @@ struct AttackPosition }; /** - * The class for a fightable monster with its own AI + * The component for a fightable monster with its own AI */ -class Monster : public Being +class MonsterComponent : public Component { public: + static const ComponentType type = CT_Monster; + /** Time in game ticks until ownership of a monster can change. */ static const int KILLSTEAL_PROTECTION_TIME = 100; - Monster(MonsterClass *); - ~Monster(); + MonsterComponent(Entity &entity, MonsterClass *); + ~MonsterComponent(); /** * Returns monster specy. @@ -286,48 +292,35 @@ class Monster : public Being /** * Performs one step of controller logic. */ - void update(); + void update(Entity &entity); - void refreshTarget(); + void refreshTarget(Entity &entity); /** - * Performs an attack + * Signal handler */ - virtual void processAttack(Attack &attack); + void monsterDied(Entity *monster); - /** - * Kills the being. - */ - void died(); + void receivedDamage(Entity *attacker, const Damage &damage, int hpLoss); /** * Alters hate for the monster */ - void changeAnger(Actor *target, int amount); + void changeAnger(Entity *target, int amount); - std::map<Being *, int> getAngerList() const; - - /** - * Calls the damage function in Being and updates the aggro list - */ - virtual int damage(Actor *source, const Damage &damage); + std::map<Entity *, int> getAngerList() const; /** * Removes a being from the anger list. */ void forgetTarget(Entity *entity); - protected: - /** - * Returns the way the actor blocks pathfinding for other objects. - */ - virtual BlockType getBlockType() const - { return BLOCKTYPE_MONSTER; } - private: static const int DECAY_TIME = 50; - int calculatePositionPriority(Point position, int targetPriority); + int calculatePositionPriority(Entity &entity, + Point position, + int targetPriority); MonsterClass *mSpecy; @@ -341,24 +334,21 @@ class Monster : public Being sigc::connection removedConnection; sigc::connection diedConnection; }; - std::map<Being *, AggressionInfo> mAnger; + std::map<Entity *, AggressionInfo> mAnger; /** * Character who currently owns this monster (killsteal protection). */ - Character *mOwner; - - /** Factor for damage mutation */ - unsigned mDamageMutation; + Entity *mOwner; /** List of characters and their skills that attacked this monster. */ - std::map<Character *, std::set <size_t> > mExpReceivers; + std::map<Entity *, std::set <size_t> > mExpReceivers; /** * List of characters who are entitled to receive exp (killsteal * protection). */ - std::set<Character *> mLegalExpReceivers; + std::set<Entity *> mLegalExpReceivers; /** * Set positions relative to target from which the monster can attack. diff --git a/src/game-server/monstercombatcomponent.cpp b/src/game-server/monstercombatcomponent.cpp new file mode 100644 index 00000000..35e2f249 --- /dev/null +++ b/src/game-server/monstercombatcomponent.cpp @@ -0,0 +1,95 @@ +/* + * The Mana Server + * Copyright (C) 2013 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 "game-server/monstercombatcomponent.h" + +#include "game-server/monster.h" +#include "scripting/scriptmanager.h" + +MonsterCombatComponent::MonsterCombatComponent(Entity &monster, + MonsterClass *specy): + CombatComponent(monster), + mDamageMutation(0) +{ + for (auto &attack : specy->getAttackInfos()) + { + addAttack(attack); + } +} + +/** + * Performs an attack + */ +void MonsterCombatComponent::processAttack(Entity *source, Attack &attack) +{ + if (!mTarget) + { + source->getComponent<BeingComponent>()->setAction(*source, STAND); + return; + } + + Damage dmg = attack.getAttackInfo()->getDamage(); + dmg.skill = 0; + dmg.base *= mDamageMutation; + dmg.delta *= mDamageMutation; + + int hit = performAttack(*mTarget, attack.getAttackInfo()->getDamage()); + + const Script::Ref &scriptCallback = + attack.getAttackInfo()->getScriptCallback(); + + if (scriptCallback.isValid() && hit > -1) + { + Script *script = ScriptManager::currentState(); + script->prepare(scriptCallback); + script->push(source); + script->push(mTarget); + script->push(hit); + script->execute(source->getMap()); + } +} + +/** + * Calls the damage function in Being and updates the aggro list + */ +int MonsterCombatComponent::damage(Entity &target, + Entity *source, + const Damage &damage) +{ + Damage newDamage = damage; + MonsterClass *specy = target.getComponent<MonsterComponent>()->getSpecy(); + float factor = specy->getVulnerability(newDamage.element); + newDamage.base = newDamage.base * factor; + newDamage.delta = newDamage.delta * factor; + int hpLoss = CombatComponent::damage(target, source, newDamage); + + + if (specy->getDamageCallback().isValid()) + { + Script *script = ScriptManager::currentState(); + script->prepare(specy->getDamageCallback()); + script->push(&target); + script->push(source); + script->push(hpLoss); + // TODO: add exact damage parameters as well + script->execute(target.getMap()); + } + return hpLoss; +} diff --git a/src/game-server/monstercombatcomponent.h b/src/game-server/monstercombatcomponent.h new file mode 100644 index 00000000..36afa26e --- /dev/null +++ b/src/game-server/monstercombatcomponent.h @@ -0,0 +1,54 @@ +/* + * The Mana Server + * Copyright (C) 2013 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/>. + */ + +#ifndef MONSTERCOMBATCOMPONENT_H +#define MONSTERCOMBATCOMPONENT_H + +#include "game-server/combatcomponent.h" + +#include "game-server/attack.h" +#include "game-server/being.h" + +class MonsterClass; + +class MonsterCombatComponent: public CombatComponent +{ +public: + MonsterCombatComponent(Entity &monster, MonsterClass *specy); + + void processAttack(Entity *source, Attack &attack); + int damage(Entity &target, Entity *source, const Damage &damage); + + void setDamageMutation(double mutation); + +private: + double mDamageMutation; +}; + +/** + * Sets the mutation of the damage compared to the default damage of the specy + * @param mutation + */ +inline void MonsterCombatComponent::setDamageMutation(double mutation) +{ + mDamageMutation = mutation; +} + +#endif /* MONSTERCOMBATCOMPONENT_H */ diff --git a/src/game-server/npc.cpp b/src/game-server/npc.cpp index 2eb73031..980358da 100644 --- a/src/game-server/npc.cpp +++ b/src/game-server/npc.cpp @@ -1,6 +1,7 @@ /* * The Mana Server * Copyright (C) 2007-2010 The Mana World Development Team + * Copyright (C) 2012 The Mana Developers * * This file is part of The Mana Server. * @@ -18,127 +19,112 @@ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. */ +#include "game-server/npc.h" + #include "game-server/character.h" #include "game-server/gamehandler.h" #include "game-server/map.h" -#include "game-server/npc.h" #include "net/messageout.h" #include "scripting/script.h" #include "scripting/scriptmanager.h" -NPC::NPC(const std::string &name, int id): - Being(OBJECT_NPC), - mID(id), +NpcComponent::NpcComponent(int npcId): + mNpcId(npcId), mEnabled(true) { - setWalkMask(Map::BLOCKMASK_WALL | Map::BLOCKMASK_MONSTER | - Map::BLOCKMASK_CHARACTER); - setName(name); } -NPC::~NPC() +NpcComponent::~NpcComponent() { Script *script = ScriptManager::currentState(); script->unref(mTalkCallback); script->unref(mUpdateCallback); } -void NPC::setEnabled(bool enabled) +void NpcComponent::setEnabled(bool enabled) { mEnabled = enabled; } -void NPC::update() +void NpcComponent::update(Entity &entity) { if (!mEnabled || !mUpdateCallback.isValid()) return; Script *script = ScriptManager::currentState(); script->prepare(mUpdateCallback); - script->push(this); - script->execute(getMap()); + script->push(&entity); + script->execute(entity.getMap()); } -void NPC::prompt(Character *ch, bool restart) +void NpcComponent::setTalkCallback(Script::Ref function) { - if (!mEnabled || !mTalkCallback.isValid()) - return; + ScriptManager::currentState()->unref(mTalkCallback); + mTalkCallback = function; +} - Script *script = ScriptManager::currentState(); +void NpcComponent::setUpdateCallback(Script::Ref function) +{ + ScriptManager::currentState()->unref(mUpdateCallback); + mUpdateCallback = function; +} - if (restart) - { - Script::Thread *thread = script->newThread(); - thread->getContext().map = getMap(); - script->prepare(mTalkCallback); - script->push(this); - script->push(ch); - ch->startNpcThread(thread, getPublicID()); - } - else - { - Script::Thread *thread = ch->getNpcThread(); - if (!thread || thread->mState != Script::ThreadPaused) - return; - script->prepareResume(thread); - ch->resumeNpcThread(); - } -} -void NPC::select(Character *ch, int index) +static Script *prepareResume(Entity *ch, Script::ThreadState expectedState) { - if (!mEnabled) - return; - - Script::Thread *thread = ch->getNpcThread(); - if (!thread || thread->mState != Script::ThreadExpectingNumber) - return; + Script::Thread *thread = + ch->getComponent<CharacterComponent>()->getNpcThread(); + if (!thread || thread->mState != expectedState) + return 0; Script *script = ScriptManager::currentState(); script->prepareResume(thread); - script->push(index); - ch->resumeNpcThread(); + return script; } -void NPC::integerReceived(Character *ch, int value) +void Npc::start(Entity *npc, Entity *ch) { - if (!mEnabled) - return; - - Script::Thread *thread = ch->getNpcThread(); - if (!thread || thread->mState != Script::ThreadExpectingNumber) - return; + NpcComponent *npcComponent = npc->getComponent<NpcComponent>(); Script *script = ScriptManager::currentState(); - script->prepareResume(thread); - script->push(value); - ch->resumeNpcThread(); + Script::Ref talkCallback = npcComponent->getTalkCallback(); + + if (npcComponent->isEnabled() && talkCallback.isValid()) + { + Script::Thread *thread = script->newThread(); + thread->getContext().map = npc->getMap(); + thread->getContext().npc = npc; + thread->getContext().character = ch; + script->prepare(talkCallback); + script->push(npc); + script->push(ch); + auto *actorComponent = npc->getComponent<ActorComponent>(); + ch->getComponent<CharacterComponent>()->startNpcThread( + thread, actorComponent->getPublicID()); + } } -void NPC::stringReceived(Character *ch, const std::string &value) +void Npc::resume(Entity *ch) { - if (!mEnabled) - return; - - Script::Thread *thread = ch->getNpcThread(); - if (!thread || thread->mState != Script::ThreadExpectingString) - return; - - Script *script = ScriptManager::currentState(); - script->prepareResume(thread); - script->push(value); - ch->resumeNpcThread(); + if (prepareResume(ch, Script::ThreadPaused)) + ch->getComponent<CharacterComponent>()->resumeNpcThread(); } -void NPC::setTalkCallback(Script::Ref function) +void Npc::integerReceived(Entity *ch, int value) { - ScriptManager::currentState()->unref(mTalkCallback); - mTalkCallback = function; + if (Script *script = prepareResume(ch, Script::ThreadExpectingNumber)) + { + script->push(value); + ch->getComponent<CharacterComponent>()->resumeNpcThread(); + } } -void NPC::setUpdateCallback(Script::Ref function) +void Npc::stringReceived(Entity *ch, const std::string &value) { - ScriptManager::currentState()->unref(mUpdateCallback); - mUpdateCallback = function; + if (Script *script = prepareResume(ch, Script::ThreadExpectingString)) + { + script->push(value); + ch->getComponent<CharacterComponent>()->resumeNpcThread(); + } } diff --git a/src/game-server/npc.h b/src/game-server/npc.h index 49dd4bfa..98fbd64d 100644 --- a/src/game-server/npc.h +++ b/src/game-server/npc.h @@ -1,6 +1,7 @@ /* * The Mana Server * Copyright (C) 2007-2010 The Mana World Development Team + * Copyright (C) 2012 The Mana Developers * * This file is part of The Mana Server. * @@ -21,26 +22,31 @@ #ifndef GAMESERVER_NPC_H #define GAMESERVER_NPC_H -#include "game-server/being.h" +#include "game-server/component.h" #include "scripting/script.h" -class Character; +class CharacterComponent; /** - * Class describing a non-player character. + * Component describing a non-player character. */ -class NPC : public Being +class NpcComponent : public Component { public: - NPC(const std::string &name, int id); + static const ComponentType type = CT_Npc; - ~NPC(); + NpcComponent(int npcId); + + ~NpcComponent(); /** * Sets the function that should be called when this NPC is talked to. */ void setTalkCallback(Script::Ref function); + Script::Ref getTalkCallback() const + { return mTalkCallback; } + /** * Sets the function that should be called each update. */ @@ -49,52 +55,57 @@ class NPC : public Being /** * Calls the update callback, if any. */ - void update(); + void update(Entity &entity); /** * Sets whether the NPC is enabled. + * + * When disabling an NPC, it does currently not cancel already started + * conversations with this NPC! */ void setEnabled(bool enabled); - /** - * Prompts NPC. - */ - void prompt(Character *, bool restart); - - /** - * Selects an NPC proposition. - */ - void select(Character *, int index); - - /** - * The player has choosen an integer. - */ - void integerReceived(Character *ch, int value); - - /** - * The player has entered an string. - */ - void stringReceived(Character *ch, const std::string &value); + bool isEnabled() const + { return mEnabled; } /** * Gets NPC ID. */ - int getNPC() const - { return mID; } - - protected: - /** - * Gets the way a monster blocks pathfinding for other objects - */ - virtual BlockType getBlockType() const - { return BLOCKTYPE_CHARACTER; } // blocks like a player character + int getNpcId() const + { return mNpcId; } private: - unsigned short mID; /**< ID of the NPC. */ - bool mEnabled; /**< Whether NPC is enabled */ + int mNpcId; + bool mEnabled; Script::Ref mTalkCallback; Script::Ref mUpdateCallback; }; + +namespace Npc { + +/** + * Starts a conversation with the NPC. + */ +void start(Entity *npc, Entity *ch); + +/** + * Resumes an NPC conversation. + */ +void resume(Entity *ch); + +/** + * The player has made a choice or entered an integer. + */ +void integerReceived(Entity *ch, int value); + +/** + * The player has entered an string. + */ +void stringReceived(Entity *ch, const std::string &value); + +} // namespace Npc + + #endif // GAMESERVER_NPC_H diff --git a/src/game-server/postman.h b/src/game-server/postman.h index b9cf7bb2..145db9e7 100644 --- a/src/game-server/postman.h +++ b/src/game-server/postman.h @@ -24,12 +24,14 @@ #include <map> #include <string> -class Character; +#include "game-server/character.h" +#include "game-server/entity.h" + class Script; struct PostCallback { - void (*handler)(Character *, + void (*handler)(Entity *, const std::string &sender, const std::string &letter, Script *); @@ -40,32 +42,34 @@ struct PostCallback class PostMan { public: - Character *getCharacter(int id) const + Entity *getCharacter(int id) const { - std::map<int, Character*>::const_iterator itr = mCharacters.find(id); + std::map<int, Entity*>::const_iterator itr = mCharacters.find(id); if (itr != mCharacters.end()) return itr->second; return 0; } - void addCharacter(Character *player) + void addCharacter(Entity *player) { - std::map<int, Character*>::iterator itr = mCharacters.find(player->getDatabaseID()); + int dataBaseId = player->getComponent<CharacterComponent>() + ->getDatabaseID(); + std::map<int, Entity*>::iterator itr = mCharacters.find(dataBaseId); if (itr == mCharacters.end()) { - mCharacters.insert(std::pair<int, Character*>(player->getDatabaseID(), player)); + mCharacters.insert(std::pair<int, Entity*>(dataBaseId, player)); } } - void getPost(Character *player, PostCallback &f) + void getPost(Entity *player, PostCallback &f) { - mCallbacks.insert(std::pair<Character*, PostCallback>(player, f)); + mCallbacks.insert(std::pair<Entity*, PostCallback>(player, f)); accountHandler->getPost(player); } - void gotPost(Character *player, std::string sender, std::string letter) + void gotPost(Entity *player, std::string sender, std::string letter) { - std::map<Character*, PostCallback>::iterator itr = mCallbacks.find(player); + std::map<Entity*, PostCallback>::iterator itr = mCallbacks.find(player); if (itr != mCallbacks.end()) { itr->second.handler(player, sender, letter, itr->second.script); @@ -73,8 +77,8 @@ public: } private: - std::map<int, Character*> mCharacters; - std::map<Character*, PostCallback> mCallbacks; + std::map<int, Entity*> mCharacters; + std::map<Entity*, PostCallback> mCallbacks; }; extern PostMan *postMan; diff --git a/src/game-server/quest.cpp b/src/game-server/quest.cpp index d6d7d92d..515e6200 100644 --- a/src/game-server/quest.cpp +++ b/src/game-server/quest.cpp @@ -36,7 +36,7 @@ typedef std::map< std::string, QuestCallbacks > PendingVariables; struct PendingQuest { - Character *character; + Entity *character; sigc::connection removedConnection; sigc::connection disconnectedConnection; PendingVariables variables; @@ -46,23 +46,27 @@ typedef std::map< int, PendingQuest > PendingQuests; static PendingQuests pendingQuests; -bool getQuestVar(Character *ch, const std::string &name, std::string &value) +bool getQuestVar(Entity *ch, const std::string &name, std::string &value) { std::map< std::string, std::string >::iterator - i = ch->questCache.find(name); - if (i == ch->questCache.end()) return false; + i = ch->getComponent<CharacterComponent>()->questCache.find(name); + if (i == ch->getComponent<CharacterComponent>()->questCache.end()) + return false; value = i->second; return true; } -void setQuestVar(Character *ch, const std::string &name, +void setQuestVar(Entity *ch, const std::string &name, const std::string &value) { + auto *characterComponent = + ch->getComponent<CharacterComponent>(); + std::map< std::string, std::string >::iterator - i = ch->questCache.lower_bound(name); - if (i == ch->questCache.end() || i->first != name) + i = characterComponent->questCache.lower_bound(name); + if (i == characterComponent->questCache.end() || i->first != name) { - ch->questCache.insert(i, std::make_pair(name, value)); + characterComponent->questCache.insert(i, std::make_pair(name, value)); } else if (i->second == value) { @@ -75,7 +79,7 @@ void setQuestVar(Character *ch, const std::string &name, accountHandler->updateCharacterVar(ch, name, value); } -void QuestRefCallback::triggerCallback(Character *ch, +void QuestRefCallback::triggerCallback(Entity *ch, const std::string &value) const { if (!mRef.isValid()) @@ -91,7 +95,7 @@ void QuestRefCallback::triggerCallback(Character *ch, static void partialRemove(Entity *t) { - int id = static_cast< Character * >(t)->getDatabaseID(); + int id = t->getComponent<CharacterComponent>()->getDatabaseID(); PendingVariables &variables = pendingQuests[id].variables; // Remove all the callbacks, but do not remove the variable names. for (PendingVariables::iterator i = variables.begin(), @@ -102,9 +106,9 @@ static void partialRemove(Entity *t) // The listener is kept in case a fullRemove is needed later. } -static void fullRemove(Character *ch) +static void fullRemove(Entity &ch) { - int id = ch->getDatabaseID(); + int id = ch.getComponent<CharacterComponent>()->getDatabaseID(); { PendingQuest &pendingQuest = pendingQuests[id]; @@ -116,11 +120,15 @@ static void fullRemove(Character *ch) pendingQuests.erase(id); } -void recoverQuestVar(Character *ch, const std::string &name, +void recoverQuestVar(Entity *ch, const std::string &name, QuestCallback *f) { - assert(ch->questCache.find(name) == ch->questCache.end()); - int id = ch->getDatabaseID(); + auto *characterComponent = + ch->getComponent<CharacterComponent>(); + + assert(characterComponent->questCache.find(name) == + characterComponent->questCache.end()); + int id = ch->getComponent<CharacterComponent>()->getDatabaseID(); PendingQuests::iterator i = pendingQuests.lower_bound(id); if (i == pendingQuests.end() || i->first != id) { @@ -134,7 +142,8 @@ void recoverQuestVar(Character *ch, const std::string &name, pendingQuest.removedConnection = ch->signal_removed.connect(sigc::ptr_fun(partialRemove)); pendingQuest.disconnectedConnection = - ch->signal_disconnected.connect(sigc::ptr_fun(fullRemove)); + characterComponent->signal_disconnected.connect( + sigc::ptr_fun(fullRemove)); i = pendingQuests.insert(i, std::make_pair(id, pendingQuest)); } @@ -162,8 +171,9 @@ void recoveredQuestVar(int id, return; } - Character *ch = pendingQuest.character; - ch->questCache[name] = value; + Entity *ch = pendingQuest.character; + auto *characterComponent = ch->getComponent<CharacterComponent>(); + characterComponent->questCache[name] = value; // Call the registered callbacks. for (QuestCallbacks::const_iterator k = j->second.begin(), diff --git a/src/game-server/quest.h b/src/game-server/quest.h index 0d130789..bfc1f874 100644 --- a/src/game-server/quest.h +++ b/src/game-server/quest.h @@ -25,7 +25,7 @@ #include "scripting/scriptmanager.h" -class Character; +class Entity; class Script; @@ -35,14 +35,14 @@ class QuestCallback virtual ~QuestCallback() { } - virtual void triggerCallback(Character *ch, + virtual void triggerCallback(Entity *ch, const std::string &value) const = 0; }; class QuestThreadCallback : public QuestCallback { public: - typedef void (*Handler)(Character *, + typedef void (*Handler)(Entity *, const std::string &value, Script *mScript); @@ -52,7 +52,7 @@ class QuestThreadCallback : public QuestCallback mScript(script) { } - void triggerCallback(Character *ch, const std::string &value) const + void triggerCallback(Entity *ch, const std::string &value) const { mHandler(ch, value, mScript); } private: @@ -67,7 +67,7 @@ class QuestRefCallback : public QuestCallback mQuestName(questName) { script->assignCallback(mRef); } - void triggerCallback(Character *ch, const std::string &value) const; + void triggerCallback(Entity *ch, const std::string &value) const; private: Script::Ref mRef; @@ -78,19 +78,18 @@ class QuestRefCallback : public QuestCallback * Gets the value associated to a quest variable. * @return false if no value was in cache. */ -bool getQuestVar(Character *, const std::string &name, std::string &value); +bool getQuestVar(Entity *, const std::string &name, std::string &value); /** * Sets the value associated to a quest variable. */ -void setQuestVar(Character *, const std::string &name, - const std::string &value); +void setQuestVar(Entity *, const std::string &name, const std::string &value); /** * Starts the recovery of a variable and returns immediatly. The callback will * be called once the value has been recovered. */ -void recoverQuestVar(Character *, const std::string &name, QuestCallback *); +void recoverQuestVar(Entity *, const std::string &name, QuestCallback *); /** * Called by the handler of the account server when a value is received. diff --git a/src/game-server/skillmanager.cpp b/src/game-server/skillmanager.cpp index 2e78194c..69a875e6 100644 --- a/src/game-server/skillmanager.cpp +++ b/src/game-server/skillmanager.cpp @@ -130,8 +130,7 @@ void SkillManager::readSkillNode(xmlNodePtr skillNode, mDefaultSkillId = skillInfo->id; } - mSkillsInfo.insert( - std::make_pair<unsigned, SkillInfo*>(skillInfo->id, skillInfo)); + mSkillsInfo.insert(std::make_pair(skillInfo->id, skillInfo)); std::string keyName = setName + "_" + skillInfo->skillName; mNamedSkillsInfo.insert(keyName, skillInfo); diff --git a/src/game-server/spawnarea.cpp b/src/game-server/spawnareacomponent.cpp index 73dca6b2..54cb017f 100644 --- a/src/game-server/spawnarea.cpp +++ b/src/game-server/spawnareacomponent.cpp @@ -18,19 +18,17 @@ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. */ -#include "game-server/spawnarea.h" +#include "game-server/spawnareacomponent.h" #include "game-server/mapcomposite.h" #include "game-server/monster.h" #include "game-server/state.h" #include "utils/logger.h" -SpawnArea::SpawnArea(MapComposite *map, - MonsterClass *specy, - const Rectangle &zone, - int maxBeings, - int spawnRate): - Entity(OBJECT_OTHER, map), +SpawnAreaComponent::SpawnAreaComponent(MonsterClass *specy, + const Rectangle &zone, + int maxBeings, + int spawnRate): mSpecy(specy), mZone(zone), mMaxBeings(maxBeings), @@ -40,14 +38,14 @@ SpawnArea::SpawnArea(MapComposite *map, { } -void SpawnArea::update() +void SpawnAreaComponent::update(Entity &entity) { if (mNextSpawn > 0) mNextSpawn--; if (mNextSpawn == 0 && mNumBeings < mMaxBeings && mSpawnRate > 0) { - MapComposite *map = getMap(); + MapComposite *map = entity.getMap(); const Map *realMap = map->getMap(); // Reset the spawn area to the whole map in case of dimensionless zone @@ -60,16 +58,21 @@ void SpawnArea::update() } // Find a free spawn location. Give up after 10 tries - int c = 10; + int triesLeft = 10; Point position; const int x = mZone.x; const int y = mZone.y; const int width = mZone.w; const int height = mZone.h; - Being *being = new Monster(mSpecy); + Entity *being = new Entity(OBJECT_MONSTER); + auto *actorComponent = new ActorComponent(*being); + being->addComponent(actorComponent); + auto *beingComponent = new BeingComponent(*being); + being->addComponent(beingComponent); + being->addComponent(new MonsterComponent(*being, mSpecy)); - if (being->getModifiedAttribute(ATTR_MAX_HP) <= 0) + if (beingComponent->getModifiedAttribute(ATTR_MAX_HP) <= 0) { LOG_WARN("Refusing to spawn dead monster " << mSpecy->getId()); delete being; @@ -81,20 +84,21 @@ void SpawnArea::update() do { position = Point(x + rand() % width, y + rand() % height); - c--; + triesLeft--; } while (!realMap->getWalk(position.x / realMap->getTileWidth(), position.y / realMap->getTileHeight(), - being->getWalkMask()) && c); + actorComponent->getWalkMask()) + && triesLeft); - if (c) + if (triesLeft) { being->signal_removed.connect( - sigc::mem_fun(this, &SpawnArea::decrease)); + sigc::mem_fun(this, &SpawnAreaComponent::decrease)); being->setMap(map); - being->setPosition(position); - being->clearDestination(); + actorComponent->setPosition(*being, position); + beingComponent->clearDestination(*being); GameState::enqueueInsert(being); mNumBeings++; @@ -114,7 +118,7 @@ void SpawnArea::update() } } -void SpawnArea::decrease(Entity *) +void SpawnAreaComponent::decrease(Entity *) { --mNumBeings; } diff --git a/src/game-server/spawnarea.h b/src/game-server/spawnareacomponent.h index 628c072e..04658448 100644 --- a/src/game-server/spawnarea.h +++ b/src/game-server/spawnareacomponent.h @@ -1,6 +1,7 @@ /* * The Mana Server * Copyright (C) 2006-2010 The Mana World Development Team + * Copyright (C) 2012 The Mana Developers * * This file is part of The Mana Server. * @@ -18,26 +19,29 @@ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SPAWNAREA_H -#define SPAWNAREA_H +#ifndef SPAWNAREACOMPONENT_H +#define SPAWNAREACOMPONENT_H + +#include "game-server/component.h" -#include "game-server/entity.h" #include "utils/point.h" -class Being; class MonsterClass; /** * A spawn area, where monsters spawn. The area is a rectangular field and will * spawn a certain number of a given monster type. */ -class SpawnArea : public Entity +class SpawnAreaComponent : public Component { public: - SpawnArea(MapComposite *, MonsterClass *, const Rectangle &zone, - int maxBeings, int spawnRate); + static const ComponentType type = CT_SpawnArea; + + SpawnAreaComponent(MonsterClass *, + const Rectangle &zone, + int maxBeings, int spawnRate); - void update(); + void update(Entity &entity); /** * Keeps track of the number of spawned being. @@ -55,4 +59,4 @@ class SpawnArea : public Entity friend struct SpawnAreaEventDispatch; }; -#endif +#endif // SPAWNAREACOMPONENT_H diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp index 6fdfb6f5..58d848de 100644 --- a/src/game-server/state.cpp +++ b/src/game-server/state.cpp @@ -22,11 +22,12 @@ #include "common/configuration.h" #include "game-server/accountconnection.h" +#include "game-server/effect.h" +#include "game-server/combatcomponent.h" #include "game-server/gamehandler.h" #include "game-server/inventory.h" #include "game-server/item.h" #include "game-server/itemmanager.h" -#include "game-server/effect.h" #include "game-server/map.h" #include "game-server/mapcomposite.h" #include "game-server/mapmanager.h" @@ -37,7 +38,6 @@ #include "scripting/script.h" #include "scripting/scriptmanager.h" #include "utils/logger.h" -#include "utils/point.h" #include "utils/speedconv.h" #include <cassert> @@ -54,11 +54,12 @@ enum */ struct DelayedEvent { - unsigned short type, x, y; + unsigned short type; + Point point; MapComposite *map; }; -typedef std::map< Actor *, DelayedEvent > DelayedEvents; +typedef std::map< Entity *, DelayedEvent > DelayedEvents; /** * The current world time in ticks since server start. @@ -78,9 +79,10 @@ static std::map< std::string, std::string > mScriptVariables; /** * Sets message fields describing character look. */ -static void serializeLooks(Character *ch, MessageOut &msg) +static void serializeLooks(Entity *ch, MessageOut &msg) { - const EquipData &equipData = ch->getPossessions().getEquipment(); + const EquipData &equipData = ch->getComponent<CharacterComponent>() + ->getPossessions().getEquipment(); // We'll use a set to check whether we already sent the update for the given // item instance. @@ -104,9 +106,7 @@ static void serializeLooks(Character *ch, MessageOut &msg) // When the insertion succeeds, its the first time // we encounter the item, so we can send the look change. // We also send empty slots for unequipment handling. - lookChanges.insert( - std::make_pair<unsigned, unsigned>(it->first, - it->second.itemId)); + lookChanges.insert(std::make_pair(it->first, it->second.itemId)); } } @@ -128,23 +128,28 @@ static void serializeLooks(Character *ch, MessageOut &msg) /** * Informs a player of what happened around the character. */ -static void informPlayer(MapComposite *map, Character *p) +static void informPlayer(MapComposite *map, Entity *p) { MessageOut moveMsg(GPMSG_BEINGS_MOVE); MessageOut damageMsg(GPMSG_BEINGS_DAMAGE); - const Point &pold = p->getOldPosition(), ppos = p->getPosition(); - int pid = p->getPublicID(), pflags = p->getUpdateFlags(); + const Point &pold = p->getComponent<BeingComponent>()->getOldPosition(); + const Point &ppos = p->getComponent<ActorComponent>()->getPosition(); + int pid = p->getComponent<ActorComponent>()->getPublicID(); + int pflags = p->getComponent<ActorComponent>()->getUpdateFlags(); int visualRange = Configuration::getValue("game_visualRange", 448); // Inform client about activities of other beings near its character for (BeingIterator it(map->getAroundBeingIterator(p, visualRange)); it; ++it) { - Being *o = *it; + Entity *o = *it; - const Point &oold = o->getOldPosition(), opos = o->getPosition(); + const Point &oold = + o->getComponent<BeingComponent>()->getOldPosition(); + const Point &opos = o->getComponent<ActorComponent>()->getPosition(); int otype = o->getType(); - int oid = o->getPublicID(), oflags = o->getUpdateFlags(); + int oid = o->getComponent<ActorComponent>()->getPublicID(); + int oflags = o->getComponent<ActorComponent>()->getUpdateFlags(); int flags = 0; // Check if the character p and the moving object o are around. @@ -166,8 +171,11 @@ static void informPlayer(MapComposite *map, Character *p) { MessageOut AttackMsg(GPMSG_BEING_ATTACK); AttackMsg.writeInt16(oid); - AttackMsg.writeInt8(o->getDirection()); - AttackMsg.writeInt8(static_cast< Being * >(o)->getAttackId()); + AttackMsg.writeInt8( + o->getComponent<BeingComponent>()->getDirection()); + CombatComponent *combatComponent = + o->getComponent<CombatComponent>(); + AttackMsg.writeInt8(combatComponent->getAttackId()); gameHandler->sendTo(p, AttackMsg); } @@ -176,7 +184,8 @@ static void informPlayer(MapComposite *map, Character *p) { MessageOut ActionMsg(GPMSG_BEING_ACTION_CHANGE); ActionMsg.writeInt16(oid); - ActionMsg.writeInt8(static_cast< Being * >(o)->getAction()); + ActionMsg.writeInt8( + o->getComponent<BeingComponent>()->getAction()); gameHandler->sendTo(p, ActionMsg); } @@ -185,18 +194,21 @@ static void informPlayer(MapComposite *map, Character *p) { MessageOut LooksMsg(GPMSG_BEING_LOOKS_CHANGE); LooksMsg.writeInt16(oid); - Character * c = static_cast<Character * >(o); - serializeLooks(c, LooksMsg); - LooksMsg.writeInt16(c->getHairStyle()); - LooksMsg.writeInt16(c->getHairColor()); - LooksMsg.writeInt16(c->getGender()); + serializeLooks(o, LooksMsg); + auto *characterComponent = + o->getComponent<CharacterComponent>(); + LooksMsg.writeInt16(characterComponent->getHairStyle()); + LooksMsg.writeInt16(characterComponent->getHairColor()); + LooksMsg.writeInt16( + o->getComponent<BeingComponent>()->getGender()); gameHandler->sendTo(p, LooksMsg); } // Send emote messages. if (oflags & UPDATEFLAG_EMOTE) { - int emoteId = o->getLastEmote(); + int emoteId = + o->getComponent<BeingComponent>()->getLastEmote(); if (emoteId > -1) { MessageOut EmoteMsg(GPMSG_BEING_EMOTE); @@ -211,15 +223,17 @@ static void informPlayer(MapComposite *map, Character *p) { MessageOut DirMsg(GPMSG_BEING_DIR_CHANGE); DirMsg.writeInt16(oid); - DirMsg.writeInt8(o->getDirection()); + DirMsg.writeInt8( + o->getComponent<BeingComponent>()->getDirection()); gameHandler->sendTo(p, DirMsg); } // Send damage messages. if (o->canFight()) { - Being *victim = static_cast< Being * >(o); - const Hits &hits = victim->getHitsTaken(); + CombatComponent *combatComponent = + o->getComponent<CombatComponent>(); + const Hits &hits = combatComponent->getHitsTaken(); for (Hits::const_iterator j = hits.begin(), j_end = hits.end(); j != j_end; ++j) { @@ -250,34 +264,41 @@ static void informPlayer(MapComposite *map, Character *p) MessageOut enterMsg(GPMSG_BEING_ENTER); enterMsg.writeInt8(otype); enterMsg.writeInt16(oid); - enterMsg.writeInt8(static_cast< Being *>(o)->getAction()); + enterMsg.writeInt8(o->getComponent<BeingComponent>()->getAction()); enterMsg.writeInt16(opos.x); enterMsg.writeInt16(opos.y); - enterMsg.writeInt8(o->getDirection()); - enterMsg.writeInt8(o->getGender()); + enterMsg.writeInt8( + o->getComponent<BeingComponent>()->getDirection()); + enterMsg.writeInt8(o->getComponent<BeingComponent>()->getGender()); switch (otype) { case OBJECT_CHARACTER: { - Character *q = static_cast< Character * >(o); - enterMsg.writeString(q->getName()); - enterMsg.writeInt8(q->getHairStyle()); - enterMsg.writeInt8(q->getHairColor()); - serializeLooks(q, enterMsg); + auto *characterComponent = + o->getComponent<CharacterComponent>(); + enterMsg.writeString( + o->getComponent<BeingComponent>()->getName()); + enterMsg.writeInt8(characterComponent->getHairStyle()); + enterMsg.writeInt8(characterComponent->getHairColor()); + serializeLooks(o, enterMsg); } break; case OBJECT_MONSTER: { - Monster *q = static_cast< Monster * >(o); - enterMsg.writeInt16(q->getSpecy()->getId()); - enterMsg.writeString(q->getName()); + MonsterComponent *monsterComponent = + o->getComponent<MonsterComponent>(); + enterMsg.writeInt16(monsterComponent->getSpecy()->getId()); + enterMsg.writeString( + o->getComponent<BeingComponent>()->getName()); } break; case OBJECT_NPC: { - NPC *q = static_cast< NPC * >(o); - enterMsg.writeInt16(q->getNPC()); - enterMsg.writeString(q->getName()); + NpcComponent *npcComponent = + o->getComponent<NpcComponent>(); + enterMsg.writeInt16(npcComponent->getNpcId()); + enterMsg.writeString( + o->getComponent<BeingComponent>()->getName()); } break; default: @@ -313,7 +334,8 @@ static void informPlayer(MapComposite *map, Character *p) // to get it within a byte with decimal precision. // For instance, a value of 4.5 will be sent as 45. moveMsg.writeInt8((unsigned short) - (o->getModifiedAttribute(ATTR_MOVE_SPEED_TPS) * 10)); + (o->getComponent<BeingComponent>() + ->getModifiedAttribute(ATTR_MOVE_SPEED_TPS) * 10)); } } @@ -325,27 +347,33 @@ static void informPlayer(MapComposite *map, Character *p) gameHandler->sendTo(p, damageMsg); // Inform client about status change. - p->sendStatus(); + p->getComponent<CharacterComponent>()->sendStatus(*p); // Inform client about health change of party members for (CharacterIterator i(map->getWholeMapIterator()); i; ++i) { - Character *c = *i; + Entity *c = *i; // Make sure its not the same character if (c == p) continue; // make sure they are in the same party - if (c->getParty() == p->getParty()) + if (c->getComponent<CharacterComponent>()->getParty() == + p->getComponent<CharacterComponent>()->getParty()) { - int cflags = c->getUpdateFlags(); + int cflags = c->getComponent<ActorComponent>()->getUpdateFlags(); if (cflags & UPDATEFLAG_HEALTHCHANGE) { + auto *beingComponent = c->getComponent<BeingComponent>(); + MessageOut healthMsg(GPMSG_BEING_HEALTH_CHANGE); - healthMsg.writeInt16(c->getPublicID()); - healthMsg.writeInt16(c->getModifiedAttribute(ATTR_HP)); - healthMsg.writeInt16(c->getModifiedAttribute(ATTR_MAX_HP)); + healthMsg.writeInt16( + c->getComponent<ActorComponent>()->getPublicID()); + healthMsg.writeInt16( + beingComponent->getModifiedAttribute(ATTR_HP)); + healthMsg.writeInt16( + beingComponent->getModifiedAttribute(ATTR_MAX_HP)); gameHandler->sendTo(p, healthMsg); } } @@ -356,12 +384,13 @@ static void informPlayer(MapComposite *map, Character *p) for (FixedActorIterator it(map->getAroundBeingIterator(p, visualRange)); it; ++it) { - assert((*it)->getType() == OBJECT_ITEM || - (*it)->getType() == OBJECT_EFFECT); + Entity *o = *it; + + assert(o->getType() == OBJECT_ITEM || + o->getType() == OBJECT_EFFECT); - Actor *o = *it; - Point opos = o->getPosition(); - int oflags = o->getUpdateFlags(); + Point opos = o->getComponent<ActorComponent>()->getPosition(); + int oflags = o->getComponent<ActorComponent>()->getUpdateFlags(); bool willBeInRange = ppos.inRangeOf(opos, visualRange); bool wereInRange = pold.inRangeOf(opos, visualRange) && !((pflags | oflags) & UPDATEFLAG_NEW_ON_MAP); @@ -372,20 +401,22 @@ static void informPlayer(MapComposite *map, Character *p) { case OBJECT_ITEM: { - Item *o = static_cast< Item * >(*it); + ItemComponent *item = o->getComponent<ItemComponent>(); + ItemClass *itemClass = item->getItemClass(); + if (oflags & UPDATEFLAG_NEW_ON_MAP) { /* Send a specific message to the client when an item appears out of nowhere, so that a sound/animation can be performed. */ MessageOut appearMsg(GPMSG_ITEM_APPEAR); - appearMsg.writeInt16(o->getItemClass()->getDatabaseID()); + appearMsg.writeInt16(itemClass->getDatabaseID()); appearMsg.writeInt16(opos.x); appearMsg.writeInt16(opos.y); gameHandler->sendTo(p, appearMsg); } else { - itemMsg.writeInt16(willBeInRange ? o->getItemClass()->getDatabaseID() : 0); + itemMsg.writeInt16(willBeInRange ? itemClass->getDatabaseID() : 0); itemMsg.writeInt16(opos.x); itemMsg.writeInt16(opos.y); } @@ -393,21 +424,23 @@ static void informPlayer(MapComposite *map, Character *p) break; case OBJECT_EFFECT: { - Effect *o = static_cast< Effect * >(*it); - o->show(); + EffectComponent *e = o->getComponent<EffectComponent>(); + e->setShown(); // Don't show old effects if (!(oflags & UPDATEFLAG_NEW_ON_MAP)) break; - Being *b = o->getBeing(); - if (b) + + if (Entity *b = e->getBeing()) { + auto *actorComponent = + b->getComponent<ActorComponent>(); MessageOut effectMsg(GPMSG_CREATE_EFFECT_BEING); - effectMsg.writeInt16(o->getEffectId()); - effectMsg.writeInt16(b->getPublicID()); + effectMsg.writeInt16(e->getEffectId()); + effectMsg.writeInt16(actorComponent->getPublicID()); gameHandler->sendTo(p, effectMsg); } else { MessageOut effectMsg(GPMSG_CREATE_EFFECT_POS); - effectMsg.writeInt16(o->getEffectId()); + effectMsg.writeInt16(e->getEffectId()); effectMsg.writeInt16(opos.x); effectMsg.writeInt16(opos.y); gameHandler->sendTo(p, effectMsg); @@ -456,11 +489,11 @@ void GameState::update(int tick) for (ActorIterator it(map->getWholeMapIterator()); it; ++it) { - Actor *a = *it; - a->clearUpdateFlags(); + Entity *a = *it; + a->getComponent<ActorComponent>()->clearUpdateFlags(); if (a->canFight()) { - static_cast< Being * >(a)->clearHitsTaken(); + a->getComponent<CombatComponent>()->clearHitsTaken(); } } } @@ -474,16 +507,15 @@ void GameState::update(int tick) it_end = delayedEvents.end(); it != it_end; ++it) { const DelayedEvent &e = it->second; - Actor *o = it->first; + Entity *o = it->first; switch (e.type) { case EVENT_REMOVE: remove(o); if (o->getType() == OBJECT_CHARACTER) { - Character *ch = static_cast< Character * >(o); - ch->disconnected(); - gameHandler->kill(ch); + o->getComponent<CharacterComponent>()->disconnected(*o); + gameHandler->kill(o); } delete o; break; @@ -494,7 +526,7 @@ void GameState::update(int tick) case EVENT_WARP: assert(o->getType() == OBJECT_CHARACTER); - warp(static_cast< Character * >(o), e.map, e.x, e.y); + warp(o, e.map, e.point); break; } } @@ -517,9 +549,9 @@ bool GameState::insert(Entity *ptr) } // Check that coordinates are actually valid. - Actor *obj = static_cast< Actor * >(ptr); + Entity *obj = static_cast< Entity * >(ptr); Map *mp = map->getMap(); - Point pos = obj->getPosition(); + Point pos = obj->getComponent<ActorComponent>()->getPosition(); if ((int)pos.x / mp->getTileWidth() >= mp->getWidth() || (int)pos.y / mp->getTileHeight() >= mp->getHeight()) { @@ -527,7 +559,7 @@ bool GameState::insert(Entity *ptr) << pos.y << " outside map " << map->getID() << '.'); // Set an arbitrary small position. pos = Point(100, 100); - obj->setPosition(pos); + obj->getComponent<ActorComponent>()->setPosition(*ptr, pos); } if (!map->insert(obj)) @@ -544,28 +576,31 @@ bool GameState::insert(Entity *ptr) { case OBJECT_ITEM: LOG_DEBUG("Item inserted: " - << static_cast<Item*>(obj)->getItemClass()->getDatabaseID()); + << obj->getComponent<ItemComponent>()->getItemClass()->getDatabaseID()); break; case OBJECT_NPC: - LOG_DEBUG("NPC inserted: " << static_cast<NPC*>(obj)->getNPC()); + LOG_DEBUG("NPC inserted: " << obj->getComponent<NpcComponent>()->getNpcId()); break; case OBJECT_CHARACTER: LOG_DEBUG("Player inserted: " - << static_cast<Being*>(obj)->getName()); + << obj->getComponent<BeingComponent>()->getName()); break; case OBJECT_EFFECT: LOG_DEBUG("Effect inserted: " - << static_cast<Effect*>(obj)->getEffectId()); + << obj->getComponent<EffectComponent>()->getEffectId()); break; case OBJECT_MONSTER: + { + MonsterComponent *monsterComponent = + obj->getComponent<MonsterComponent>(); LOG_DEBUG("Monster inserted: " - << static_cast<Monster*>(obj)->getSpecy()->getId()); + << monsterComponent->getSpecy()->getId()); break; - + } case OBJECT_ACTOR: case OBJECT_OTHER: default: @@ -573,7 +608,8 @@ bool GameState::insert(Entity *ptr) break; } - obj->raiseUpdateFlags(UPDATEFLAG_NEW_ON_MAP); + obj->getComponent<ActorComponent>()->raiseUpdateFlags( + UPDATEFLAG_NEW_ON_MAP); if (obj->getType() != OBJECT_CHARACTER) return true; @@ -584,11 +620,11 @@ bool GameState::insert(Entity *ptr) mapChangeMessage.writeString(map->getName()); mapChangeMessage.writeInt16(pos.x); mapChangeMessage.writeInt16(pos.y); - gameHandler->sendTo(static_cast< Character * >(obj), mapChangeMessage); + gameHandler->sendTo(ptr, mapChangeMessage); // update the online state of the character - accountHandler->updateOnlineStatus( - static_cast< Character * >(obj)->getDatabaseID(), true); + accountHandler->updateOnlineStatus(ptr->getComponent<CharacterComponent>() + ->getDatabaseID(), true); return true; } @@ -618,28 +654,31 @@ void GameState::remove(Entity *ptr) { case OBJECT_ITEM: LOG_DEBUG("Item removed: " - << static_cast<Item*>(ptr)->getItemClass()->getDatabaseID()); + << ptr->getComponent<ItemComponent>()->getItemClass()->getDatabaseID()); break; case OBJECT_NPC: - LOG_DEBUG("NPC removed: " << static_cast<NPC*>(ptr)->getNPC()); + LOG_DEBUG("NPC removed: " << ptr->getComponent<NpcComponent>()->getNpcId()); break; case OBJECT_CHARACTER: LOG_DEBUG("Player removed: " - << static_cast<Being*>(ptr)->getName()); + << ptr->getComponent<BeingComponent>()->getName()); break; case OBJECT_EFFECT: LOG_DEBUG("Effect removed: " - << static_cast<Effect*>(ptr)->getEffectId()); + << ptr->getComponent<EffectComponent>()->getEffectId()); break; case OBJECT_MONSTER: + { + MonsterComponent *monsterComponent = + ptr->getComponent<MonsterComponent>(); LOG_DEBUG("Monster removed: " - << static_cast<Monster*>(ptr)->getSpecy()->getId()); + << monsterComponent->getSpecy()->getId()); break; - + } case OBJECT_ACTOR: case OBJECT_OTHER: default: @@ -651,22 +690,24 @@ void GameState::remove(Entity *ptr) { if (ptr->getType() == OBJECT_CHARACTER) { - static_cast< Character * >(ptr)->cancelTransaction(); + auto *characterComponent = + ptr->getComponent<CharacterComponent>(); + characterComponent->cancelTransaction(); // remove characters online status accountHandler->updateOnlineStatus( - static_cast< Character * >(ptr)->getDatabaseID(), false); + characterComponent->getDatabaseID(), false); } - Actor *obj = static_cast< Actor * >(ptr); MessageOut msg(GPMSG_BEING_LEAVE); - msg.writeInt16(obj->getPublicID()); - Point objectPos = obj->getPosition(); + msg.writeInt16(ptr->getComponent<ActorComponent>()->getPublicID()); + Point objectPos = ptr->getComponent<ActorComponent>()->getPosition(); - for (CharacterIterator p(map->getAroundActorIterator(obj, visualRange)); + for (CharacterIterator p(map->getAroundActorIterator(ptr, visualRange)); p; ++p) { - if (*p != obj && objectPos.inRangeOf((*p)->getPosition(), + if (*p != ptr && objectPos.inRangeOf( + (*p)->getComponent<ActorComponent>()->getPosition(), visualRange)) { gameHandler->sendTo(*p, msg); @@ -675,16 +716,17 @@ void GameState::remove(Entity *ptr) } else if (ptr->getType() == OBJECT_ITEM) { - Item *obj = static_cast< Item * >(ptr); - Point pos = obj->getPosition(); + Point pos = ptr->getComponent<ActorComponent>()->getPosition(); MessageOut msg(GPMSG_ITEMS); msg.writeInt16(0); msg.writeInt16(pos.x); msg.writeInt16(pos.y); - for (CharacterIterator p(map->getAroundActorIterator(obj, visualRange)); p; ++p) + for (CharacterIterator p(map->getAroundActorIterator(ptr, visualRange)); p; ++p) { - if (pos.inRangeOf((*p)->getPosition(), visualRange)) + const Point &point = + (*p)->getComponent<ActorComponent>()->getPosition(); + if (pos.inRangeOf(point, visualRange)) { gameHandler->sendTo(*p, msg); } @@ -694,27 +736,31 @@ void GameState::remove(Entity *ptr) map->remove(ptr); } -void GameState::warp(Character *ptr, MapComposite *map, int x, int y) +void GameState::warp(Entity *ptr, MapComposite *map, const Point &point) { remove(ptr); + ptr->setMap(map); - ptr->setPosition(Point(x, y)); - ptr->clearDestination(); + ptr->getComponent<ActorComponent>()->setPosition(*ptr, point); + ptr->getComponent<BeingComponent>()->clearDestination(*ptr); /* Force update of persistent data on map change, so that characters can respawn at the start of the map after a death or a disconnection. */ accountHandler->sendCharacterData(ptr); + auto *characterComponent = + ptr->getComponent<CharacterComponent>(); + // If the player has just left, The character pointer is also about // to be deleted. So we don't have to do anything else. - if (!ptr->isConnected()) + if (!characterComponent->isConnected()) return; if (map->isActive()) { if (!insert(ptr)) { - ptr->disconnected(); + characterComponent->disconnected(*ptr); gameHandler->kill(ptr); delete ptr; } @@ -722,7 +768,7 @@ void GameState::warp(Character *ptr, MapComposite *map, int x, int y) else { MessageOut msg(GAMSG_REDIRECT); - msg.writeInt32(ptr->getDatabaseID()); + msg.writeInt32(characterComponent->getDatabaseID()); accountHandler->send(msg); gameHandler->prepareServerChange(ptr); } @@ -731,7 +777,7 @@ void GameState::warp(Character *ptr, MapComposite *map, int x, int y) /** * Enqueues an event. It will be executed at end of update. */ -static void enqueueEvent(Actor *ptr, const DelayedEvent &e) +static void enqueueEvent(Entity *ptr, const DelayedEvent &e) { std::pair< DelayedEvents::iterator, bool > p = delayedEvents.insert(std::make_pair(ptr, e)); @@ -742,47 +788,58 @@ static void enqueueEvent(Actor *ptr, const DelayedEvent &e) } } -void GameState::enqueueInsert(Actor *ptr) +void GameState::enqueueInsert(Entity *ptr) { - DelayedEvent e = { EVENT_INSERT, 0, 0, 0 }; - enqueueEvent(ptr, e); + DelayedEvent event; + event.type = EVENT_INSERT; + event.map = 0; + enqueueEvent(ptr, event); } -void GameState::enqueueRemove(Actor *ptr) +void GameState::enqueueRemove(Entity *ptr) { - DelayedEvent e = { EVENT_REMOVE, 0, 0, 0 }; - enqueueEvent(ptr, e); + DelayedEvent event; + event.type = EVENT_REMOVE; + event.map = 0; + enqueueEvent(ptr, event); } -void GameState::enqueueWarp(Character *ptr, MapComposite *m, int x, int y) +void GameState::enqueueWarp(Entity *ptr, + MapComposite *map, + const Point &point) { // When the player has just disconnected, better not wait for the pointer // to become invalid. - if (!ptr->isConnected()) + if (!ptr->getComponent<CharacterComponent>()->isConnected()) { - warp(ptr, m, x, y); + warp(ptr, map, point); return; } - DelayedEvent e = { EVENT_WARP, x, y, m }; - enqueueEvent(ptr, e); + DelayedEvent event; + event.type = EVENT_WARP; + event.point = point; + event.map = map; + enqueueEvent(ptr, event); } -void GameState::sayAround(Actor *obj, const std::string &text) +void GameState::sayAround(Entity *entity, const std::string &text) { - Point speakerPosition = obj->getPosition(); + Point speakerPosition = entity->getComponent<ActorComponent>()->getPosition(); int visualRange = Configuration::getValue("game_visualRange", 448); - for (CharacterIterator i(obj->getMap()->getAroundActorIterator(obj, visualRange)); i; ++i) + for (CharacterIterator i(entity->getMap()->getAroundActorIterator(entity, visualRange)); i; ++i) { - if (speakerPosition.inRangeOf((*i)->getPosition(), visualRange)) + const Point &point = + (*i)->getComponent<ActorComponent>()->getPosition(); + if (speakerPosition.inRangeOf(point, visualRange)) { - sayTo(*i, obj, text); + sayTo(*i, entity, text); } } } -void GameState::sayTo(Actor *destination, Actor *source, const std::string &text) +void GameState::sayTo(Entity *destination, Entity *source, const std::string &text) { if (destination->getType() != OBJECT_CHARACTER) return; //only characters will read it anyway @@ -798,11 +855,11 @@ void GameState::sayTo(Actor *destination, Actor *source, const std::string &text } else { - msg.writeInt16(static_cast< Actor * >(source)->getPublicID()); + msg.writeInt16(source->getComponent<ActorComponent>()->getPublicID()); } msg.writeString(text); - gameHandler->sendTo(static_cast< Character * >(destination), msg); + gameHandler->sendTo(destination, msg); } void GameState::sayToAll(const std::string &text) diff --git a/src/game-server/state.h b/src/game-server/state.h index 34eb8814..147a57cb 100644 --- a/src/game-server/state.h +++ b/src/game-server/state.h @@ -18,16 +18,16 @@ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SERVER_STATE_H -#define SERVER_STATE_H +#ifndef STATE_H +#define STATE_H + +#include "utils/point.h" #include <string> -class MapComposite; class Entity; -class Actor; -class Character; - +class ItemClass; +class MapComposite; namespace GameState { @@ -69,37 +69,37 @@ namespace GameState * @note No update may be in progress. * @note The character is destroyed, if needed. */ - void warp(Character *, MapComposite *, int x, int y); + void warp(Entity *, MapComposite *, const Point &point); /** * Enqueues an insert event. * @note The event will be executed at end of update. */ - void enqueueInsert(Actor *); + void enqueueInsert(Entity *); /** * Enqueues a remove event. * @note The event will be executed at end of update. * @note The entity will be destroyed at that time. */ - void enqueueRemove(Actor *); + void enqueueRemove(Entity *); /** * Enqueues a warp event. * @note The event will be executed at end of update. */ - void enqueueWarp(Character *, MapComposite *, int x, int y); + void enqueueWarp(Entity *, MapComposite *, const Point &point); /** * Says something to an actor. * @note passing NULL as source generates a message from "Server:" */ - void sayTo(Actor *destination, Actor *source, const std::string &text); + void sayTo(Entity *destination, Entity *source, const std::string &text); /** * Says something to everything around an actor. */ - void sayAround(Actor *, const std::string &text); + void sayAround(Entity *entity, const std::string &text); /** * Says something to every player on the server. @@ -107,21 +107,21 @@ namespace GameState void sayToAll(const std::string &text); /** - * Gets the cached value of a global script variable + * Gets the cached value of a global script variable. */ std::string getVariable(const std::string &key); /** * Changes a global script variable and notifies the database server - * about the change + * about the change. */ - void setVariable (const std::string &key, const std::string &value); + void setVariable(const std::string &key, const std::string &value); /** * Changes a global variable without notifying the database server - * about the change + * about the change. */ - void setVariableFromDbserver (const std::string &key, const std::string &value); + void setVariableFromDbserver(const std::string &key, const std::string &value); /** * Informs all maps about the change of a variable so the maps can call @@ -131,4 +131,4 @@ namespace GameState const std::string &value); } -#endif +#endif // STATE_H diff --git a/src/game-server/statuseffect.cpp b/src/game-server/statuseffect.cpp index b5b988f4..75fae986 100644 --- a/src/game-server/statuseffect.cpp +++ b/src/game-server/statuseffect.cpp @@ -32,14 +32,14 @@ StatusEffect::~StatusEffect() { } -void StatusEffect::tick(Being *target, int count) +void StatusEffect::tick(Entity &target, int count) { if (mTickCallback.isValid()) { Script *s = ScriptManager::currentState(); s->prepare(mTickCallback); - s->push(target); + s->push(&target); s->push(count); - s->execute(target->getMap()); + s->execute(target.getMap()); } } diff --git a/src/game-server/statuseffect.h b/src/game-server/statuseffect.h index 7da5fdf9..3c38944b 100644 --- a/src/game-server/statuseffect.h +++ b/src/game-server/statuseffect.h @@ -23,7 +23,7 @@ #include "scripting/script.h" -class Being; +class Entity; class StatusEffect { @@ -31,7 +31,7 @@ class StatusEffect StatusEffect(int id); ~StatusEffect(); - void tick(Being *target, int count); + void tick(Entity &target, int count); int getId() const { return mId; } diff --git a/src/game-server/trade.cpp b/src/game-server/trade.cpp index e1779d2f..3fbcd0e6 100644 --- a/src/game-server/trade.cpp +++ b/src/game-server/trade.cpp @@ -37,34 +37,35 @@ * TRADE_AGREE_WAIT : One player has agreed, waiting for the other one */ -Trade::Trade(Character *c1, Character *c2): +Trade::Trade(Entity *c1, Entity *c2): mChar1(c1), mChar2(c2), mMoney1(0), mMoney2(0), mState(TRADE_INIT), mCurrencyId(ATTR_GP) { MessageOut msg(GPMSG_TRADE_REQUEST); - msg.writeInt16(c1->getPublicID()); - c2->getClient()->send(msg); - c1->setTrading(this); - c2->setTrading(this); + msg.writeInt16(c1->getComponent<ActorComponent>()->getPublicID()); + c2->getComponent<CharacterComponent>()->getClient()->send(msg); + c1->getComponent<CharacterComponent>()->setTrading(this); + c2->getComponent<CharacterComponent>()->setTrading(this); } Trade::~Trade() { - mChar1->setTrading(NULL); - mChar2->setTrading(NULL); + mChar1->getComponent<CharacterComponent>()->setTrading(nullptr); + mChar2->getComponent<CharacterComponent>()->setTrading(nullptr); } void Trade::cancel() { MessageOut msg(GPMSG_TRADE_CANCEL); - mChar1->getClient()->send(msg); - mChar2->getClient()->send(msg); + mChar1->getComponent<CharacterComponent>()->getClient()->send(msg); + mChar2->getComponent<CharacterComponent>()->getClient()->send(msg); delete this; } -bool Trade::request(Character *c, int id) +bool Trade::request(Entity *c, int id) { //The trade isn't confirmed, the player which is request is the same. - if (mState != TRADE_INIT || c != mChar2 || mChar1->getPublicID() != id) + if (mState != TRADE_INIT || c != mChar2 || + mChar1->getComponent<ActorComponent>()->getPublicID() != id) { /* This is not an ack for the current transaction. So assume a new one is about to start and cancel the current one. */ @@ -79,8 +80,8 @@ bool Trade::request(Character *c, int id) //Telling both player that the trade has started MessageOut msg(GPMSG_TRADE_START); - mChar1->getClient()->send(msg); - mChar2->getClient()->send(msg); + mChar1->getComponent<CharacterComponent>()->getClient()->send(msg); + mChar2->getComponent<CharacterComponent>()->getClient()->send(msg); return true; } @@ -99,7 +100,7 @@ bool Trade::perform(TradedItems items, Inventory &inv1, Inventory &inv2) return true; } -void Trade::agree(Character *c) +void Trade::agree(Entity *c) { // No player agreed if (mState == TRADE_CONFIRMED) @@ -116,7 +117,7 @@ void Trade::agree(Character *c) // Send the other player that the first player has confirmed MessageOut msg(GPMSG_TRADE_AGREED); - mChar2->getClient()->send(msg); + mChar2->getComponent<CharacterComponent>()->getClient()->send(msg); return; } @@ -131,15 +132,23 @@ void Trade::agree(Character *c) // Check if both player has the objects in their inventories // and enouth money, then swap them. Inventory v1(mChar1), v2(mChar2); - if (mChar1->getAttribute(mCurrencyId) >= mMoney1 - mMoney2 && - mChar2->getAttribute(mCurrencyId) >= mMoney2 - mMoney1 && + + const double moneyChar1 = mChar1->getComponent<BeingComponent>() + ->getAttributeBase(mCurrencyId); + const double moneyChar2 = mChar2->getComponent<BeingComponent>() + ->getAttributeBase(mCurrencyId); + + if (moneyChar1 >= mMoney1 - mMoney2 && + moneyChar2 >= mMoney2 - mMoney1 && perform(mItems1, v1, v2) && perform(mItems2, v2, v1)) { - mChar1->setAttribute(mCurrencyId, mChar1->getAttribute(mCurrencyId) - - mMoney1 + mMoney2); - mChar2->setAttribute(mCurrencyId, mChar2->getAttribute(mCurrencyId) - - mMoney2 + mMoney1); + mChar1->getComponent<BeingComponent>() + ->setAttribute(*mChar1, mCurrencyId, + moneyChar1 - mMoney1 + mMoney2); + mChar2->getComponent<BeingComponent>() + ->setAttribute(*mChar2, mCurrencyId, + moneyChar2 - mMoney2 + mMoney1); } else { @@ -148,12 +157,12 @@ void Trade::agree(Character *c) } MessageOut msg(GPMSG_TRADE_COMPLETE); - mChar1->getClient()->send(msg); - mChar2->getClient()->send(msg); + mChar1->getComponent<CharacterComponent>()->getClient()->send(msg); + mChar2->getComponent<CharacterComponent>()->getClient()->send(msg); delete this; } -void Trade::confirm(Character *c) +void Trade::confirm(Entity *c) { if (mState == TRADE_CONFIRMED || mState == TRADE_AGREE_WAIT) return; @@ -173,7 +182,7 @@ void Trade::confirm(Character *c) //Send the other player that the first player has confirmed MessageOut msg(GPMSG_TRADE_CONFIRM); - mChar2->getClient()->send(msg); + mChar2->getComponent<CharacterComponent>()->getClient()->send(msg); return; } @@ -185,11 +194,11 @@ void Trade::confirm(Character *c) mState = TRADE_CONFIRMED; MessageOut msg(GPMSG_TRADE_BOTH_CONFIRM); - mChar1->getClient()->send(msg); - mChar2->getClient()->send(msg); + mChar1->getComponent<CharacterComponent>()->getClient()->send(msg); + mChar2->getComponent<CharacterComponent>()->getClient()->send(msg); } -void Trade::setMoney(Character *c, int amount) +void Trade::setMoney(Entity *c, int amount) { //If the player has already confirmed, exit. if ((mState != TRADE_RUN && (mState != TRADE_CONFIRM_WAIT || c != mChar1)) @@ -205,26 +214,26 @@ void Trade::setMoney(Character *c, int amount) if (c == mChar1) { mMoney1 = amount; - mChar2->getClient()->send(msg); + mChar2->getComponent<CharacterComponent>()->getClient()->send(msg); } else { assert(c == mChar2); mMoney2 = amount; - mChar1->getClient()->send(msg); + mChar1->getComponent<CharacterComponent>()->getClient()->send(msg); } // Go back to normal run. mState = TRADE_RUN; } -void Trade::addItem(Character *c, int slot, int amount) +void Trade::addItem(Entity *c, int slot, int amount) { //If the player has already confirmed, exit. if ((mState != TRADE_RUN && (mState != TRADE_CONFIRM_WAIT || c != mChar1)) || amount < 0) return; - Character *other; + Entity *other; TradedItems *items; if (c == mChar1) { @@ -249,11 +258,14 @@ void Trade::addItem(Character *c, int slot, int amount) later on. At worst, the transaction will be canceled at the end if the client lied. */ - TradedItem ti = { id, slot, amount }; - items->push_back(ti); + TradedItem tradedItem; + tradedItem.id = id; + tradedItem.slot = slot; + tradedItem.amount = amount; + items->push_back(tradedItem); MessageOut msg(GPMSG_TRADE_ADD_ITEM); msg.writeInt16(id); msg.writeInt8(amount); - other->getClient()->send(msg); + other->getComponent<CharacterComponent>()->getClient()->send(msg); } diff --git a/src/game-server/trade.h b/src/game-server/trade.h index 43b674fb..72ec0b3d 100644 --- a/src/game-server/trade.h +++ b/src/game-server/trade.h @@ -23,7 +23,7 @@ #include <vector> -class Character; +class Entity; class Inventory; class Trade @@ -34,7 +34,7 @@ class Trade * Sets up a trade between two characters. * Asks for an acknowledgment from the second one. */ - Trade(Character *, Character *); + Trade(Entity *, Entity *); /** * Cancels a trade by a given character (optional). @@ -49,27 +49,27 @@ class Trade * otherwise. * @return true if the current trade keeps going. */ - bool request(Character *, int); + bool request(Entity *, int); /** * Confirm the trade. */ - void confirm(Character *); + void confirm(Entity *); /* * Agree to complete the trade */ - void agree(Character *c); + void agree(Entity *c); /** * Adds some items to the trade. */ - void addItem(Character *, int slot, int amount); + void addItem(Entity *, int slot, int amount); /** * Adds some money to the trade. */ - void setMoney(Character *, int amount); + void setMoney(Entity *, int amount); private: @@ -77,8 +77,9 @@ class Trade struct TradedItem { - unsigned short id; - unsigned char slot, amount; + unsigned id; + unsigned slot; + int amount; }; typedef std::vector< TradedItem > TradedItems; @@ -97,7 +98,7 @@ class Trade static bool perform(TradedItems items, Inventory &inv1, Inventory &inv2); - Character *mChar1, *mChar2; /**< Characters involved. */ + Entity *mChar1, *mChar2; /**< Characters involved. */ TradedItems mItems1, mItems2; /**< Traded items. */ int mMoney1, mMoney2; /**< Traded money. */ TradeState mState; /**< State of transaction. */ diff --git a/src/game-server/trigger.cpp b/src/game-server/triggerareacomponent.cpp index 10e50ecb..b599546b 100644 --- a/src/game-server/trigger.cpp +++ b/src/game-server/triggerareacomponent.cpp @@ -1,6 +1,7 @@ /* * The Mana Server * Copyright (C) 2006-2010 The Mana World Development Team + * Copyright (C) 2012 The Mana Developers * * This file is part of The Mana Server. * @@ -18,7 +19,7 @@ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. */ -#include "game-server/trigger.h" +#include "game-server/triggerareacomponent.h" #include "game-server/character.h" #include "game-server/mapcomposite.h" @@ -29,11 +30,11 @@ #include <cassert> -void WarpAction::process(Actor *obj) +void WarpAction::process(Entity *obj) { if (obj->getType() == OBJECT_CHARACTER) { - GameState::enqueueWarp(static_cast< Character * >(obj), mMap, mX, mY); + GameState::enqueueWarp(obj, mMap, mTargetPoint); } } @@ -45,7 +46,7 @@ ScriptAction::ScriptAction(Script *script, Script::Ref callback, int arg) : assert(mCallback.isValid()); } -void ScriptAction::process(Actor *obj) +void ScriptAction::process(Entity *obj) { LOG_DEBUG("Script trigger area activated: " << "(" << obj << ", " << mArg << ")"); @@ -56,20 +57,24 @@ void ScriptAction::process(Actor *obj) mScript->execute(obj->getMap()); } -void TriggerArea::update() +void TriggerAreaComponent::update(Entity &entity) { - std::set<Actor*> insideNow; - for (BeingIterator i(getMap()->getInsideRectangleIterator(mZone)); i; ++i) + MapComposite *map = entity.getMap(); + std::set<Entity *> insideNow; + + for (BeingIterator i(map->getInsideRectangleIterator(mZone)); i; ++i) { - // Don't deal with unitialized actors. - if (!(*i) || !(*i)->isPublicIdValid()) + // Don't deal with uninitialized actors + if (!(*i) || !(*i)->getComponent<ActorComponent>()->isPublicIdValid()) continue; // The BeingIterator returns the mapZones in touch with the rectangle // area. On the other hand, the beings contained in the map zones // may not be within the rectangle area. Hence, this additional // contains() condition. - if (mZone.contains((*i)->getPosition())) + const Point &point = + (*i)->getComponent<ActorComponent>()->getPosition(); + if (mZone.contains(point)) { insideNow.insert(*i); diff --git a/src/game-server/trigger.h b/src/game-server/triggerareacomponent.h index fbd41fa8..3d2a8412 100644 --- a/src/game-server/trigger.h +++ b/src/game-server/triggerareacomponent.h @@ -1,6 +1,7 @@ /* * The Mana Server * Copyright (C) 2006-2010 The Mana World Development Team + * Copyright (C) 2012 The Mana Developers * * This file is part of The Mana Server. * @@ -18,33 +19,35 @@ * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef TRIGGER_H -#define TRIGGER_H +#ifndef TRIGGERAREACOMPONENT_H +#define TRIGGERAREACOMPONENT_H #include "game-server/entity.h" #include "scripting/script.h" #include "utils/point.h" -class Actor; +#include <set> + +class Entity; class TriggerAction { public: virtual ~TriggerAction() {} - virtual void process(Actor *obj) = 0; + virtual void process(Entity *obj) = 0; }; class WarpAction : public TriggerAction { public: - WarpAction(MapComposite *m, int x, int y) - : mMap(m), mX(x), mY(y) {} + WarpAction(MapComposite *m, const Point &point) + : mMap(m), mTargetPoint(point) {} - virtual void process(Actor *obj); + virtual void process(Entity *obj); private: MapComposite *mMap; - unsigned short mX, mY; + Point mTargetPoint; }; class ScriptAction : public TriggerAction @@ -52,7 +55,7 @@ class ScriptAction : public TriggerAction public: ScriptAction(Script *script, Script::Ref callback, int arg); - virtual void process(Actor *obj); + virtual void process(Entity *obj); private: Script *mScript; // Script object to be called @@ -60,22 +63,29 @@ class ScriptAction : public TriggerAction int mArg; // Argument passed to script function (meaning is function-specific) }; -class TriggerArea : public Entity +class TriggerAreaComponent : public Component { public: + static const ComponentType type = CT_TriggerArea; + /** * Creates a rectangular trigger for a given map. */ - TriggerArea(MapComposite *m, const Rectangle &r, TriggerAction *ptr, bool once) - : Entity(OBJECT_OTHER, m), mZone(r), mAction(ptr), mOnce(once) {} + TriggerAreaComponent(const Rectangle &r, + TriggerAction *ptr, + bool once) : + mZone(r), + mAction(ptr), + mOnce(once) + {} - virtual void update(); + void update(Entity &entity); private: Rectangle mZone; TriggerAction *mAction; bool mOnce; - std::set<Actor *> mInside; + std::set<Entity *> mInside; }; -#endif +#endif // TRIGGERAREACOMPONENT_H diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 838ced6d..8f4a0512 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -1,7 +1,7 @@ /* * The Mana Server * Copyright (C) 2007-2010 The Mana World Development Team - * Copyright (C) 2010 The Mana Developers + * Copyright (C) 2010-2013 The Mana Developers * * This file is part of The Mana Server. * @@ -33,6 +33,7 @@ extern "C" { #include "game-server/buysell.h" #include "game-server/character.h" #include "game-server/collisiondetection.h" +#include "game-server/combatcomponent.h" #include "game-server/effect.h" #include "game-server/gamehandler.h" #include "game-server/inventory.h" @@ -49,7 +50,7 @@ extern "C" { #include "game-server/state.h" #include "game-server/statuseffect.h" #include "game-server/statusmanager.h" -#include "game-server/trigger.h" +#include "game-server/triggerareacomponent.h" #include "net/messageout.h" #include "scripting/luautil.h" #include "scripting/luascript.h" @@ -87,7 +88,7 @@ extern "C" { static int on_update_derived_attribute(lua_State *s) { luaL_checktype(s, 1, LUA_TFUNCTION); - Being::setUpdateDerivedAttributesCallback(getScript(s)); + BeingComponent::setUpdateDerivedAttributesCallback(getScript(s)); return 0; } @@ -106,7 +107,7 @@ static int on_update_derived_attribute(lua_State *s) static int on_recalculate_base_attribute(lua_State *s) { luaL_checktype(s, 1, LUA_TFUNCTION); - Being::setRecalculateBaseAttributeCallback(getScript(s)); + BeingComponent::setRecalculateBaseAttributeCallback(getScript(s)); return 0; } @@ -119,7 +120,7 @@ static int on_recalculate_base_attribute(lua_State *s) static int on_character_death(lua_State *s) { luaL_checktype(s, 1, LUA_TFUNCTION); - Character::setDeathCallback(getScript(s)); + CharacterComponent::setDeathCallback(getScript(s)); return 0; } @@ -133,7 +134,7 @@ static int on_character_death(lua_State *s) static int on_character_death_accept(lua_State *s) { luaL_checktype(s, 1, LUA_TFUNCTION); - Character::setDeathAcceptedCallback(getScript(s)); + CharacterComponent::setDeathAcceptedCallback(getScript(s)); return 0; } @@ -146,7 +147,7 @@ static int on_character_death_accept(lua_State *s) static int on_character_login(lua_State *s) { luaL_checktype(s, 1, LUA_TFUNCTION); - Character::setLoginCallback(getScript(s)); + CharacterComponent::setLoginCallback(getScript(s)); return 0; } @@ -163,13 +164,13 @@ static int on_being_death(lua_State *s) return 0; } -/** LUA on_being_remove (callbacks) - * on_being_remove(function ref) +/** LUA on_entity_remove (callbacks) + * on_entity_remove(function ref) ** * Will make sure that the function ''ref'' gets called with the being * as argument as soon a being gets removed from a map. */ -static int on_being_remove(lua_State *s) +static int on_entity_remove(lua_State *s) { luaL_checktype(s, 1, LUA_TFUNCTION); LuaScript::setRemoveNotificationCallback(getScript(s)); @@ -247,7 +248,8 @@ static int on_mapupdate(lua_State *s) */ /** LUA npc_create (creation) - * npc_create(string name, int spriteID, int gender, int x, int y, function talkfunct, function updatefunct) + * npc_create(string name, int spriteID, int gender, int x, int y, + * function talkfunct, function updatefunct) ** * **Return value:** A handle to the created NPC. * @@ -280,25 +282,39 @@ static int npc_create(lua_State *s) MapComposite *m = checkCurrentMap(s); - NPC *q = new NPC(name, id); - q->setGender(getGender(gender)); - q->setMap(m); - q->setPosition(Point(x, y)); + NpcComponent *npcComponent = new NpcComponent(id); + + Entity *npc = new Entity(OBJECT_NPC); + auto *actorComponent = new ActorComponent(*npc); + npc->addComponent(actorComponent); + auto *beingComponent = new BeingComponent(*npc); + npc->addComponent(beingComponent); + npc->addComponent(npcComponent); + // some health so it doesn't spawn dead + beingComponent->setAttribute(*npc, ATTR_MAX_HP, 100); + beingComponent->setAttribute(*npc, ATTR_HP, 100); + beingComponent->setName(name); + beingComponent->setGender(getGender(gender)); + + actorComponent->setWalkMask(Map::BLOCKMASK_WALL | Map::BLOCKMASK_MONSTER | + Map::BLOCKMASK_CHARACTER); + npc->setMap(m); + actorComponent->setPosition(*npc, Point(x, y)); if (lua_isfunction(s, 6)) { lua_pushvalue(s, 6); - q->setTalkCallback(luaL_ref(s, LUA_REGISTRYINDEX)); + npcComponent->setTalkCallback(luaL_ref(s, LUA_REGISTRYINDEX)); } if (lua_isfunction(s, 7)) { lua_pushvalue(s, 7); - q->setUpdateCallback(luaL_ref(s, LUA_REGISTRYINDEX)); + npcComponent->setUpdateCallback(luaL_ref(s, LUA_REGISTRYINDEX)); } - GameState::enqueueInsert(q); - lua_pushlightuserdata(s, q); + GameState::enqueueInsert(npc); + push(s, npc); return 1; } @@ -309,9 +325,9 @@ static int npc_create(lua_State *s) */ static int npc_enable(lua_State *s) { - NPC *p = checkNPC(s, 1); - p->setEnabled(true); - GameState::enqueueInsert(p); + Entity *npc = checkNpc(s, 1); + npc->getComponent<NpcComponent>()->setEnabled(true); + GameState::enqueueInsert(npc); return 0; } @@ -322,9 +338,9 @@ static int npc_enable(lua_State *s) */ static int npc_disable(lua_State *s) { - NPC *p = checkNPC(s, 1); - p->setEnabled(false); - GameState::remove(p); + Entity *npc = checkNpc(s, 1); + npc->getComponent<NpcComponent>()->setEnabled(false); + GameState::remove(npc); return 0; } @@ -344,36 +360,33 @@ static int monster_create(lua_State *s) const int y = luaL_checkint(s, 3); MapComposite *m = checkCurrentMap(s); - Monster *q = new Monster(monsterClass); - q->setMap(m); - q->setPosition(Point(x, y)); - GameState::enqueueInsert(q); + Entity *monster = new Entity(OBJECT_MONSTER); + auto *actorComponent = new ActorComponent(*monster); + monster->addComponent(actorComponent); + monster->addComponent(new BeingComponent(*monster)); + monster->addComponent(new MonsterComponent(*monster, monsterClass)); + monster->setMap(m); + actorComponent->setPosition(*monster, Point(x, y)); + GameState::enqueueInsert(monster); - lua_pushlightuserdata(s, q); + push(s, monster); return 1; } -/** LUA monster_remove (creation) - * monster_remove(handle monster) +/** LUA entity:remove (creation) + * entity:remove() ** - * **Return value:** True if removing the monster suceeded. - * - * Remove the monster ''monster'' from the current map. + * Removes the entity from its current map. */ -static int monster_remove(lua_State *s) +static int entity_remove(lua_State *s) { - bool monsterRemoved = false; - if (Monster *m = getMonster(s, 1)) - { - GameState::remove(m); - monsterRemoved = true; - } - lua_pushboolean(s, monsterRemoved); - return 1; + GameState::remove(LuaEntity::check(s, 1)); + return 0; } /** LUA trigger_create (creation) - * trigger_create(int x, int y, int width, int height, function trigger_function, int arg, bool once) + * trigger_create(int x, int y, int width, int height, + * function trigger_function, int arg, bool once) ** * Creates a new trigger area with the given ''height'' and ''width'' in pixels * at the map position ''x'':''y'' in pixels. When a being steps into this area @@ -407,14 +420,18 @@ static int trigger_create(lua_State *s) script->assignCallback(function); lua_pop(s, 1); + Entity *triggerEntity = new Entity(OBJECT_OTHER, m); + ScriptAction *action = new ScriptAction(script, function, id); Rectangle r = { x, y, width, height }; - TriggerArea *area = new TriggerArea(m, r, action, once); + TriggerAreaComponent *area = new TriggerAreaComponent(r, action, once); + + triggerEntity->addComponent(area); LOG_INFO("Created script trigger at " << x << "," << y << " (" << width << "x" << height << ") id: " << id); - bool ret = GameState::insert(area); + bool ret = GameState::insertOrDelete(triggerEntity); lua_pushboolean(s, ret); return 1; } @@ -438,8 +455,8 @@ static int effect_create(lua_State *s) if (lua_isuserdata(s, 2)) { // being mode - Being *b = checkBeing(s, 2); - Effects::show(id, b->getMap(), b); + Entity *b = checkBeing(s, 2); + Effects::show(id, b); } else { @@ -467,45 +484,43 @@ static int item_drop(lua_State *s) const int number = luaL_optint(s, 4, 1); MapComposite *map = checkCurrentMap(s); - Item *i = new Item(ic, number); - - i->setMap(map); - Point pos(x, y); - i->setPosition(pos); - GameState::enqueueInsert(i); + Entity *item = Item::create(map, Point(x, y), ic, number); + GameState::enqueueInsert(item); return 0; } /** LUA_CATEGORY Input and output (input) */ -/** LUA npc_message (input) - * npc_message(handle npc, handle character, string message) +/** LUA say (input) + * say(string message) ** * **Warning:** May only be called from an NPC talk function. * - * Shows an NPC dialog box on the screen of character ''ch'' displaying the - * string ''msg''. Idles the current thread until the user click "OK". + * Shows an NPC dialog box on the screen of displaying the string ''message''. + * Idles the current thread until the user click "OK". */ -static int npc_message(lua_State *s) +static int say(lua_State *s) { - NPC *p = checkNPC(s, 1); - Character *q = checkCharacter(s, 2); - const char *m = luaL_checkstring(s, 3); + const char *m = luaL_checkstring(s, 1); Script::Thread *thread = checkCurrentThread(s); + Entity *npc = thread->getContext().npc; + Entity *character = thread->getContext().character; + if (!(npc && character)) + luaL_error(s, "not in npc conversation"); MessageOut msg(GPMSG_NPC_MESSAGE); - msg.writeInt16(p->getPublicID()); + msg.writeInt16(npc->getComponent<ActorComponent>()->getPublicID()); msg.writeString(m); - gameHandler->sendTo(q, msg); + gameHandler->sendTo(character, msg); thread->mState = Script::ThreadPaused; return lua_yield(s, 0); } -/** LUA npc_choice (input) - * npc_choice(handle npc, handle character, item1, item2, ... itemN) +/** LUA ask (input) + * ask(item1, item2, ... itemN) ** * **Return value:** Number of the option the player selected (starting with 1). * @@ -513,23 +528,24 @@ static int npc_message(lua_State *s) * * Shows an NPC dialog box on the users screen with a number of dialog options * to choose from. Idles the current thread until the user selects one or - * aborts the current thread when the user clicks "cancel". + * aborts the current thread when the user clicks "cancel". * * Items are either strings or tables of strings (indices are ignored, * but presumed to be taken in order). So, - * ''npc_choice(npc, ch, "A", {"B", "C", "D"}, "E")'' is the same as - * ''npc_choice(npc, ch, "A", "B", "C", "D", "E")''. + * ''ask("A", {"B", "C", "D"}, "E")'' is the same as + * ''ask("A", "B", "C", "D", "E")''. */ -static int npc_choice(lua_State *s) +static int ask(lua_State *s) { - NPC *p = checkNPC(s, 1); - Character *q = checkCharacter(s, 2); - Script::Thread *thread = checkCurrentThread(s); + Entity *npc = thread->getContext().npc; + Entity *character = thread->getContext().character; + if (!(npc && character)) + luaL_error(s, "not in npc conversation"); MessageOut msg(GPMSG_NPC_CHOICE); - msg.writeInt16(p->getPublicID()); - for (int i = 3, i_end = lua_gettop(s); i <= i_end; ++i) + msg.writeInt16(npc->getComponent<ActorComponent>()->getPublicID()); + for (int i = 1, i_end = lua_gettop(s); i <= i_end; ++i) { if (lua_isstring(s, i)) { @@ -546,8 +562,7 @@ static int npc_choice(lua_State *s) } else { - luaL_error(s, "npc_choice called " - "with incorrect parameters."); + luaL_error(s, "ask called with incorrect parameters."); return 0; } lua_pop(s, 1); @@ -555,18 +570,18 @@ static int npc_choice(lua_State *s) } else { - luaL_error(s, "npc_choice called with incorrect parameters."); + luaL_error(s, "ask called with incorrect parameters."); return 0; } } - gameHandler->sendTo(q, msg); + gameHandler->sendTo(character, msg); thread->mState = Script::ThreadExpectingNumber; return lua_yield(s, 0); } -/** LUA npc_ask_integer (input) - * npc_ask_integer(handle npc, handle character, min_num, max_num, [default_num]) +/** LUA ask_number (input) + * ask_number(min_num, max_num, [default_num]) ** * **Return value:** The number the player entered into the field. * @@ -576,29 +591,31 @@ static int npc_choice(lua_State *s) * ''min_num'' and ''max_num''. If ''default_num'' is set this number will be * uses as default. Otherwise ''min_num'' will be the default. */ -static int npc_ask_integer(lua_State *s) +static int ask_number(lua_State *s) { - NPC *p = checkNPC(s, 1); - Character *q = checkCharacter(s, 2); - int min = luaL_checkint(s, 3); - int max = luaL_checkint(s, 4); - int defaultValue = luaL_optint(s, 5, min); + int min = luaL_checkint(s, 1); + int max = luaL_checkint(s, 2); + int defaultValue = luaL_optint(s, 3, min); Script::Thread *thread = checkCurrentThread(s); + Entity *npc = thread->getContext().npc; + Entity *character = thread->getContext().character; + if (!(npc && character)) + luaL_error(s, "not in npc conversation"); MessageOut msg(GPMSG_NPC_NUMBER); - msg.writeInt16(p->getPublicID()); + msg.writeInt16(npc->getComponent<ActorComponent>()->getPublicID()); msg.writeInt32(min); msg.writeInt32(max); msg.writeInt32(defaultValue); - gameHandler->sendTo(q, msg); + gameHandler->sendTo(character, msg); thread->mState = Script::ThreadExpectingNumber; return lua_yield(s, 0); } -/** LUA npc_ask_string (input) - * npc_ask_string(handle npc, handle character) +/** LUA ask_string (input) + * ask_string() ** * **Return value:** The string the player entered. * @@ -606,64 +623,67 @@ static int npc_ask_integer(lua_State *s) * * Shows a dialog box to a user which allows him to enter a text. */ -static int npc_ask_string(lua_State *s) +static int ask_string(lua_State *s) { - NPC *p = checkNPC(s, 1); - Character *q = checkCharacter(s, 2); - Script::Thread *thread = checkCurrentThread(s); + Entity *npc = thread->getContext().npc; + Entity *character = thread->getContext().character; + if (!(npc && character)) + luaL_error(s, "not in npc conversation"); MessageOut msg(GPMSG_NPC_STRING); - msg.writeInt16(p->getPublicID()); - gameHandler->sendTo(q, msg); + msg.writeInt16(npc->getComponent<ActorComponent>()->getPublicID()); + gameHandler->sendTo(character, msg); thread->mState = Script::ThreadExpectingString; return lua_yield(s, 0); } /** LUA npc_post (input) - * npc_post(handle npc, handle character) + * npc_post() ** * Starts retrieving post. Better not try to use it so far. */ static int npc_post(lua_State *s) { - NPC *p = checkNPC(s, 1); - Character *q = checkCharacter(s, 2); + const Script::Context *context = getScript(s)->getContext(); + Entity *npc = context->npc; + Entity *character = context->character; MessageOut msg(GPMSG_NPC_POST); - msg.writeInt16(p->getPublicID()); - gameHandler->sendTo(q, msg); + msg.writeInt16(npc->getComponent<ActorComponent>()->getPublicID()); + gameHandler->sendTo(character, msg); return 0; } -/** LUA being_say (input) - * being_say(handle being, string message) +/** LUA entity:say (input) + * entity:say(string message) ** - * Makes ''being'', which can be a character, monster or NPC, speak the string - * ''message'' as if it was entered by a player in the chat bar. + * Makes this entity (which can be a character, monster or NPC), speak the + * string ''message'' as if it was entered by a player in the chat bar. */ -static int being_say(lua_State *s) +static int entity_say(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *actor = checkActor(s, 1); const char *message = luaL_checkstring(s, 2); - GameState::sayAround(being, message); + GameState::sayAround(actor, message); return 0; } -/** LUA chat_message (input) - * chat_message(handle character, string message) +/** LUA entity:message (input) + * entity:message(string message) ** - * Outputs the string ''message'' in the chatlog of ''character'' which will - * appear as a private message from "Server". + * Delivers the string ''message'' to this entity (which needs to be a + * character). It will appear in the chatlog as a private message from + * "Server". */ -static int chat_message(lua_State *s) +static int entity_message(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *character = checkCharacter(s, 1); const char *message = luaL_checkstring(s, 2); - GameState::sayTo(being, NULL, message); + GameState::sayTo(character, nullptr, message); return 0; } @@ -690,25 +710,25 @@ static int announce(lua_State *s) /** LUA_CATEGORY Inventory interaction (inventory) */ -/** LUA npc_trade (inventory) - * npc_trade(handle npc, - * handle character, - * bool mode, - * {int item1id, int item1amount, int item1cost}, ..., - * {int itemNid, int itemNamount, int itemNcost}) - * npc_trade(handle npc, - * handle character, - * bool mode, - * {string item1name, int item1amount, int item1cost}, ..., - * {string itemNname, int itemNamount, int itemNcost}) +/** LUA trade (inventory) + * trade(bool mode, + * { int item1id, int item1amount, int item1cost }, ..., + * { int itemNid, int itemNamount, int itemNcost }) + * trade(bool mode, + * { string item1name, int item1amount, int item1cost }, ..., + * { string itemNname, int itemNamount, int itemNcost }) ** * FIXME: Move into a seperate file - * Opens a trade window for ''character'' while talking with ''npc''. ''mode'' is true for selling and false for buying. You have to set each items the NPC is buying/selling, the cost and the maximum amount in {}. + * Opens a trade window from an NPC conversation. ''mode'' + * is true for selling and false for buying. You have to set each items the NPC + * is buying/selling, the cost and the maximum amount in {}. * - * **Note:** If the fourth parameters (table type) is omitted or invalid, and the mode set to sell (true), + * **Note:** If the fourth parameters (table type) is omitted or invalid, and + * the mode set to sell (true), * the whole player inventory is then sellable. * - * **N.B.:** Be sure to put a ''value'' (item cost) parameter in your items.xml to permit the player to sell it when using this option. + * **N.B.:** Be sure to put a ''value'' (item cost) parameter in your items.xml + * to permit the player to sell it when using this option. * * **Return values:** * * **0** when a trade has been started @@ -716,56 +736,68 @@ static int announce(lua_State *s) * * **2** in case of errors. * * **Examples:** - * <code lua npc_trade.lua> + * <code lua trade.lua> * -- "A buy sample." - * local buycase = npc_trade(npc, ch, false, { {"Sword", 10, 20}, {"Bow", 10, 30}, {"Dagger", 10, 50} }) + * local buycase = trade(false, { + * {"Sword", 10, 20}, + * {"Bow", 10, 30}, + * {"Dagger", 10, 50} + * }) * if buycase == 0 then - * npc_message(npc, ch, "What do you want to buy?") + * npc_message("What do you want to buy?") * elseif buycase == 1 then - * npc_message(npc, ch, "I've got no items to sell.") + * npc_message("I've got no items to sell.") * else - * npc_message(npc, ch, "Hmm, something went wrong... Ask a scripter to fix the buying mode!") + * npc_message("Hmm, something went wrong... Ask a scripter to + * fix the buying mode!") * end * * -- ... * * -- "Example: Let the player sell only pre-determined items." - * local sellcase = npc_trade(npc, ch, true, { {"Sword", 10, 20}, {"Bow", 10, 30}, - * {"Dagger", 10, 200}, {"Knife", 10, 300}, {"Arrow", 10, 500}, {"Cactus Drink", 10, 25} }) + * local sellcase = trade(true, { + * {"Sword", 10, 20}, + * {"Bow", 10, 30}, + * {"Dagger", 10, 200}, + * {"Knife", 10, 300}, + * {"Arrow", 10, 500}, + * {"Cactus Drink", 10, 25} + * }) * if sellcase == 0 then - * npc_message(npc, ch, "Here we go:") + * npc_message("Here we go:") * elseif sellcase == 1 then - * npc_message(npc, ch, "I'm not interested by your items.") + * npc_message("I'm not interested by your items.") * else - * npc_message(npc, ch, "Hmm, something went wrong... Ask a scripter to fix me!") + * npc_message("Hmm, something went wrong...") + * npc_message("Ask a scripter to fix me!") * end * * -- ... * - * -- "Example: Let the player sell every item with a 'value' parameter in the server's items.xml file - * local sellcase = npc_trade(npc, ch, true) + * -- "Example: Let the player sell every item with a 'value' parameter in + * the server's items.xml file + * local sellcase = trade(true) * if sellcase == 0 then - * npc_message(npc, ch, "Ok, what do you want to sell:") + * npc_message("Ok, what do you want to sell:") * elseif sellcase == 1 then - * npc_message(npc, ch, "I'm not interested by any of your items.") + * npc_message("I'm not interested by any of your items.") * else - * npc_message(npc, ch, "Hmm, something went wrong... Ask a scripter to fix this!") + * npc_message("Hmm, something went wrong...") + * npc_message("Ask a scripter to fix me!") * end * </code> */ -static int npc_trade(lua_State *s) +static int trade(lua_State *s) { - NPC *p = checkNPC(s, 1); - Character *q = checkCharacter(s, 2); - if (!lua_isboolean(s, 3)) - { - luaL_error(s, "npc_trade called with incorrect parameters."); - return 0; - } + const Script::Context *context = getScript(s)->getContext(); + Entity *npc = context->npc; + Entity *character = context->character; - bool sellMode = lua_toboolean(s, 3); - BuySell *t = new BuySell(q, sellMode); - if (!lua_istable(s, 4)) + luaL_argcheck(s, lua_isboolean(s, 1), 1, "boolean expected"); + bool sellMode = lua_toboolean(s, 1); + + BuySell *t = new BuySell(character, sellMode); + if (!lua_istable(s, 2)) { if (sellMode) { @@ -778,7 +810,7 @@ static int npc_trade(lua_State *s) return 1; } - if (t->start(p)) + if (t->start(npc)) { lua_pushinteger(s, 0); return 1; @@ -791,7 +823,7 @@ static int npc_trade(lua_State *s) } else { - raiseWarning(s, "npc_trade[Buy] called with invalid " + raiseWarning(s, "trade[Buy] called with invalid " "or empty items table parameter."); t->cancel(); lua_pushinteger(s, 2); @@ -802,11 +834,11 @@ static int npc_trade(lua_State *s) int nbItems = 0; lua_pushnil(s); - while (lua_next(s, 4)) + while (lua_next(s, 2)) { if (!lua_istable(s, -1)) { - raiseWarning(s, "npc_trade called with invalid " + raiseWarning(s, "trade called with invalid " "or empty items table parameter."); t->cancel(); lua_pushinteger(s, 2); @@ -823,7 +855,7 @@ static int npc_trade(lua_State *s) if (!it) { - raiseWarning(s, "npc_trade called with incorrect " + raiseWarning(s, "trade called with incorrect " "item id or name."); t->cancel(); lua_pushinteger(s, 2); @@ -833,7 +865,7 @@ static int npc_trade(lua_State *s) } else if (!lua_isnumber(s, -1)) { - raiseWarning(s, "npc_trade called with incorrect parameters " + raiseWarning(s, "trade called with incorrect parameters " "in item table."); t->cancel(); lua_pushinteger(s, 2); @@ -856,7 +888,7 @@ static int npc_trade(lua_State *s) lua_pushinteger(s, 1); return 1; } - if (t->start(p)) + if (t->start(npc)) { lua_pushinteger(s, 0); return 1; @@ -868,22 +900,26 @@ static int npc_trade(lua_State *s) } } -/** LUA chr_inv_count (inventory) - * chr_inv_count(handle character, bool inInventory, bool inEquipment, int id1, ..., int idN) - * chr_inv_count(handle character, bool inInventory, bool inEquipment, string name1, ..., string nameN) +/** LUA entity:inv_count (inventory) + * entity:inv_count(bool inInventory, bool inEquipment, + * int id1, ..., int idN) + * entity:inv_count(bool inInventory, bool inEquipment, + * string name1, ..., string nameN) ** + * Valid only for character entities. + * * The boolean values ''inInventory'' and ''inEquipment'' make possible to * select whether equipped or carried items must be counted. * * **Return values:** A number of integers with the amount of items ''id'' or - * ''name'' carried or equipped by the ''character''. + * ''name'' carried or equipped by the character. */ -static int chr_inv_count(lua_State *s) +static int entity_inv_count(lua_State *s) { - Character *q = checkCharacter(s, 1); + Entity *q = checkCharacter(s, 1); if (!lua_isboolean(s, 2) || !lua_isboolean(s, 3)) { - luaL_error(s, "chr_inv_count called with incorrect parameters."); + luaL_error(s, "inv_count called with incorrect parameters."); return 0; } @@ -903,32 +939,34 @@ static int chr_inv_count(lua_State *s) return nb_items; } -/** LUA chr_inv_change (inventory) - * chr_inv_change(handle character, int id1, int number1, ..., int idN, numberN) - * chr_inv_change(handle character, string name1, int number1, ..., string nameN, numberN) +/** LUA entity:inv_change (inventory) + * entity:inv_change(int id1, int number1, ..., int idN, numberN) + * entity:inv_change(string name1, int number1, ..., string nameN, numberN) ** - * **Return value:** Boolean true on success, boolean false on failure. + * Valid only for character entities. * * Changes the number of items with the item ID ''id'' or ''name'' owned by - * ''character'' by ''number''. You can change any number of items with this - * function by passing multiple ''id'' or ''name'' and ''number'' pairs. + * this character by ''number''. You can change any number of items with this + * function by passing multiple ''id'' or ''name'' and ''number'' pairs. * A failure can be caused by trying to take items the character doesn't possess. * + * **Return value:** Boolean true on success, boolean false on failure. + * * **Warning:** When one of the operations fails the following operations are * ignored but these before are executed. For that reason you should always - * check if the character possesses items you are taking away using - * chr_inv_count. + * check if the character possesses items you are taking away using + * entity:inv_count. */ -static int chr_inv_change(lua_State *s) +static int entity_inv_change(lua_State *s) { - Character *q = checkCharacter(s, 1); + Entity *q = checkCharacter(s, 1); int nb_items = (lua_gettop(s) - 1) / 2; Inventory inv(q); for (int i = 0; i < nb_items; ++i) { if (!lua_isnumber(s, i * 2 + 3)) { - luaL_error(s, "chr_inv_change called with " + luaL_error(s, "inv_change called with " "incorrect parameters."); return 0; } @@ -943,8 +981,10 @@ static int chr_inv_change(lua_State *s) nb = inv.remove(id, -nb); if (nb) { - LOG_WARN("chr_inv_change() removed more items than owned: " - << "character: " << q->getName() << " item id: " << id); + LOG_WARN("inv_change removed more items than owned: " + << "character: " + << q->getComponent<BeingComponent>()->getName() + << " item id: " << id); } } else @@ -952,9 +992,9 @@ static int chr_inv_change(lua_State *s) nb = inv.insert(id, nb); if (nb) { - Item *item = new Item(ic, nb); - item->setMap(q->getMap()); - item->setPosition(q->getPosition()); + const Point &position = + q->getComponent<ActorComponent>()->getPosition(); + Entity *item = Item::create(q->getMap(), position, ic, nb); GameState::enqueueInsert(item); } } @@ -963,19 +1003,22 @@ static int chr_inv_change(lua_State *s) return 1; } -/** LUA chr_get_inventory (inventory) - * chr_get_inventory(character): table[]{slot, item id, name, amount} +/** LUA entity:inventory (inventory) + * entity:inventory(): table[]{slot, item id, name, amount} ** - * used to get a full view of a character's inventory. - * This is not the preferred way to know whether an item is in the character's inventory: - * Use chr_inv_count for simple cases. + * Valid only for character entities. + * + * Used to get a full view of a character's inventory. + * This is not the preferred way to know whether an item is in the character's + * inventory: + * Use entity:inv_count for simple cases. * - * **Return value:** A table containing all the info about the character's inventory. - * Empty slots are not listed. + * **Return value:** A table containing all the info about the character's + * inventory. Empty slots are not listed. * * **Example of use:** * <code lua> - * local inventory_table = chr_get_inventory(ch) + * local inventory_table = ch:inventory() * for i = 1, #inventory_table do * item_message = item_message.."\n"..inventory_table[i].slot..", " * ..inventory_table[i].id..", "..inventory_table[i].name..", " @@ -983,12 +1026,13 @@ static int chr_inv_change(lua_State *s) * end * </code> */ -static int chr_get_inventory(lua_State *s) +static int entity_get_inventory(lua_State *s) { - Character *q = checkCharacter(s, 1); + Entity *q = checkCharacter(s, 1); // Create a lua table with the inventory ids. - const InventoryData invData = q->getPossessions().getInventory(); + const InventoryData invData = q->getComponent<CharacterComponent>() + ->getPossessions().getInventory(); lua_newtable(s); int firstTableStackPosition = lua_gettop(s); @@ -1028,31 +1072,34 @@ static int chr_get_inventory(lua_State *s) return 1; } -/** LUA chr_get_equipment (inventory) - * chr_get_equipment(character): table[](slot, item id, name)} +/** LUA entity:equipment (inventory) + * entity:equipment(): table[](slot, item id, name)} ** + * Valid only for character entities. + * * Used to get a full view of a character's equipment. * This is not the preferred way to know whether an item is equipped: - * Use chr_inv_count for simple cases. + * Use entity:inv_count for simple cases. * - * **Return value:** A table containing all the info about the character's equipment. - * Empty slots are not listed. + * **Return value:** A table containing all the info about the character's + * equipment. Empty slots are not listed. * * **Example of use:** * <code lua> - * local equipment_table = chr_get_equipment(ch) + * local equipment_table = ch:equipment() * for i = 1, #equipment_table do * item_message = item_message.."\n"..equipment_table[i].slot..", " * ..equipment_table[i].id..", "..equipment_table[i].name * end * </code> */ -static int chr_get_equipment(lua_State *s) +static int entity_get_equipment(lua_State *s) { - Character *q = checkCharacter(s, 1); + Entity *q = checkCharacter(s, 1); // Create a lua table with the inventory ids. - const EquipData equipData = q->getPossessions().getEquipment(); + const EquipData equipData = q->getComponent<CharacterComponent>() + ->getPossessions().getEquipment(); lua_newtable(s); int firstTableStackPosition = lua_gettop(s); @@ -1094,14 +1141,16 @@ static int chr_get_equipment(lua_State *s) return 1; } -/** LUA chr_equip_slot (inventory) - * chr_equip_slot(handle character, int slot) +/** LUA entity:equip_slot (inventory) + * entity:equip_slot(int slot) ** + * Valid only for character entities. + * * Makes the character equip the item in the given inventory slot. */ -static int chr_equip_slot(lua_State *s) +static int entity_equip_slot(lua_State *s) { - Character *ch = checkCharacter(s, 1); + Entity *ch = checkCharacter(s, 1); int inventorySlot = luaL_checkint(s, 2); Inventory inv(ch); @@ -1109,18 +1158,20 @@ static int chr_equip_slot(lua_State *s) return 1; } -/** LUA chr_equip_item (inventory) - * chr_equip_item(handle character, int item_id) - * chr_equip_item(handle character, string item_name) +/** LUA entity:equip_item (inventory) + * entity:equip_item(int item_id) + * entity:equip_item(string item_name) ** - * Makes the character equip the item id when it's existing - * in the player's inventory. + * Valid only for character entities. + * + * Makes the character equip the item id when it exists in the player's + * inventory. * * **Return value:** true if equipping suceeded. false otherwise. */ -static int chr_equip_item(lua_State *s) +static int entity_equip_item(lua_State *s) { - Character *ch = checkCharacter(s, 1); + Entity *ch = checkCharacter(s, 1); ItemClass *it = checkItemClass(s, 2); Inventory inv(ch); @@ -1135,16 +1186,18 @@ static int chr_equip_item(lua_State *s) return 1; } -/** LUA chr_unequip_slot (inventory) - * chr_unequip_slot(handle character, int slot) +/** LUA entity:unequip_slot (inventory) + * entity:unequip_slot(int slot) ** + * Valid only for character entities. + * * Makes the character unequip the item in the given equipment slot. * * **Return value:** true upon success. false otherwise. */ -static int chr_unequip_slot(lua_State *s) +static int entity_unequip_slot(lua_State *s) { - Character *ch = checkCharacter(s, 1); + Entity *ch = checkCharacter(s, 1); int equipmentSlot = luaL_checkint(s, 2); Inventory inv(ch); @@ -1153,18 +1206,20 @@ static int chr_unequip_slot(lua_State *s) return 1; } -/** LUA chr_unequip_item (inventory) - * chr_unequip_item(handle character, int item_id) - * chr_unequip_item(handle character, string item_name) +/** LUA entity:unequip_item (inventory) + * entity:unequip_item(int item_id) + * entity:unequip_item(string item_name) ** - * Makes the character unequip the item(s) corresponding to the id - * when it's existing in the player's equipment. + * Valid only for character entities. + * + * Makes the character unequip the item(s) corresponding to the id when it + * exists in the player's equipment. * * **Return value:** true when every item were unequipped from equipment. */ -static int chr_unequip_item(lua_State *s) +static int entity_unequip_item(lua_State *s) { - Character *ch = checkCharacter(s, 1); + Entity *ch = checkCharacter(s, 1); ItemClass *it = checkItemClass(s, 2); Inventory inv(ch); @@ -1185,7 +1240,7 @@ static int chr_unequip_item(lua_State *s) */ static int chr_get_quest(lua_State *s) { - Character *q = checkCharacter(s, 1); + Entity *q = checkCharacter(s, 1); const char *name = luaL_checkstring(s, 2); luaL_argcheck(s, name[0] != 0, 2, "empty variable name"); @@ -1214,7 +1269,7 @@ static int chr_get_quest(lua_State *s) */ static int chr_set_quest(lua_State *s) { - Character *q = checkCharacter(s, 1); + Entity *q = checkCharacter(s, 1); const char *name = luaL_checkstring(s, 2); const char *value = luaL_checkstring(s, 3); luaL_argcheck(s, name[0] != 0, 2, "empty variable name"); @@ -1223,138 +1278,168 @@ static int chr_set_quest(lua_State *s) return 0; } -/** LUA chr_set_special_recharge_speed (being) - * chr_set_special_recharge_speed(handle ch, int specialid, int new_speed) - * chr_set_special_recharge_speed(handle ch, string specialname, int new_speed) +/** LUA entity:set_special_recharge_speed (being) + * entity:set_special_recharge_speed(int specialid, int new_speed) + * entity:set_special_recharge_speed(string specialname, int new_speed) ** + * Valid only for character entities. + * * Sets the recharge speed of the special to a new value for the character. * * **Note:** When passing the ''specialname'' as parameter make sure that it is * formatted in this way: <setname>_<specialname> (for eg. "Magic_Healingspell"). */ -static int chr_set_special_recharge_speed(lua_State *s) +static int entity_set_special_recharge_speed(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); const int special = checkSpecial(s, 2); const int speed = luaL_checkint(s, 3); - if (!c->setSpecialRechargeSpeed(special, speed)) + if (!c->getComponent<CharacterComponent>() + ->setSpecialRechargeSpeed(special, speed)) { luaL_error(s, - "chr_set_special_recharge_speed called with special " + "set_special_recharge_speed called with special " "that is not owned by character."); } return 0; } -/** LUA chr_get_special_recharge_speed (being) - * chr_get_special_recharge_speed(handle ch, int specialid) - * chr_get_special_recharge_speed(handle ch, string specialname) +/** LUA entity:special_recharge_speed (being) + * entity:special_recharge_speed(int specialid) + * entity:special_recharge_speed(string specialname) ** + * Valid only for character entities. + * * **Return value:** The current recharge speed of the special that is owned by - * the character ''ch''. + * the character. * * **Note:** When passing the ''specialname'' as parameter make sure that it is * formatted in this way: <setname>_<specialname> (for eg. "Magic_Healingspell"). */ -static int chr_get_special_recharge_speed(lua_State *s) +static int entity_get_special_recharge_speed(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); const int special = checkSpecial(s, 2); - SpecialMap::iterator it = c->findSpecial(special); + auto *characterComponent = c->getComponent<CharacterComponent>(); - luaL_argcheck(s, it != c->getSpecialEnd(), 2, + SpecialMap::iterator it = characterComponent->findSpecial(special); + + luaL_argcheck(s, it != characterComponent->getSpecialEnd(), 2, "character does not have special"); lua_pushinteger(s, it->second.rechargeSpeed); return 1; } -/** LUA chr_set_special_mana (being) - * chr_set_special_mana(handle ch, int specialid, int new_mana) - * chr_set_special_mana(handle ch, string specialname, int new_mana) +/** LUA entity:set_special_mana (being) + * entity:set_special_mana(int specialid, int new_mana) + * entity:set_special_mana(string specialname, int new_mana) ** + * Valid only for character entities. + * * Sets the mana (recharge status) of the special to a new value for the * character. * * **Note:** When passing the ''specialname'' as parameter make sure that it is * formatted in this way: <setname>_<specialname> (for eg. "Magic_Healingspell"). */ -static int chr_set_special_mana(lua_State *s) +static int entity_set_special_mana(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); const int special = checkSpecial(s, 2); const int mana = luaL_checkint(s, 3); - if (!c->setSpecialMana(special, mana)) + if (!c->getComponent<CharacterComponent>()->setSpecialMana(special, mana)) { luaL_error(s, - "chr_set_special_mana called with special " + "special_mana called with special " "that is not owned by character."); } return 0; } -/** LUA chr_get_special_mana (being) - * chr_get_special_mana(handle ch, int specialid) - * chr_get_special_mana(handle ch, string specialname) +/** LUA entity:special_mana (being) + * entity:special_mana(int specialid) + * entity:special_mana(string specialname) ** * **Return value:** The mana (recharge status) of the special that is owned by - * the character ''ch''. + * the character. * * **Note:** When passing the ''specialname'' as parameter make sure that it is * formatted in this way: <setname>_<specialname> (for eg. "Magic_Healingspell"). */ -static int chr_get_special_mana(lua_State *s) +static int entity_get_special_mana(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); + auto *characterComponent = c->getComponent<CharacterComponent>(); const int special = checkSpecial(s, 2); - SpecialMap::iterator it = c->findSpecial(special); - luaL_argcheck(s, it != c->getSpecialEnd(), 2, + SpecialMap::iterator it = characterComponent->findSpecial(special); + luaL_argcheck(s, it != characterComponent->getSpecialEnd(), 2, "character does not have special"); lua_pushinteger(s, it->second.currentMana); return 1; } -/** LUA being_walk (being) - * being_walk(handle being, int pixelX, int pixelY [, int walkSpeed]) +/** LUA entity:walk (being) + * entity:walk(int pixelX, int pixelY [, int walkSpeed]) ** - * Set the desired destination in pixels for the **'being'**. + * Valid only for being entities. + * + * Set the desired destination in pixels for the being. * * The optional **'WalkSpeed'** is to be given in tiles per second. The average * speed is 6.0 tiles per second. If no speed is given the default speed of the * being is used. */ -static int being_walk(lua_State *s) +static int entity_walk(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); const int x = luaL_checkint(s, 2); const int y = luaL_checkint(s, 3); - being->setDestination(Point(x, y)); + auto *beingComponent = being->getComponent<BeingComponent>(); + + beingComponent->setDestination(*being, Point(x, y)); if (lua_gettop(s) >= 4) { - being->setAttribute(ATTR_MOVE_SPEED_TPS, luaL_checknumber(s, 4)); - being->setAttribute(ATTR_MOVE_SPEED_RAW, utils::tpsToRawSpeed( - being->getModifiedAttribute(ATTR_MOVE_SPEED_TPS))); + const double speedTps = luaL_checknumber(s, 4); + beingComponent->setAttribute(*being, ATTR_MOVE_SPEED_TPS, speedTps); + const double modifiedSpeedTps = + beingComponent->getModifiedAttribute(ATTR_MOVE_SPEED_TPS); + beingComponent->setAttribute(*being, ATTR_MOVE_SPEED_RAW, + utils::tpsToRawSpeed(modifiedSpeedTps)); } return 0; } -/** LUA being_damage (being) - * being_damage(handle being, int damage, int delta, - * int accuracy, int type, int element) - * being_damage(handle being, int damage, int delta, int accuracy, - * int type, int element, handle source) - * being_damage(handle being, int damage, int delta, int accuracy, - * int type, int element, handle source, int skill) - * being_damage(handle being, int damage, int delta, int accuracy, - * int type, int element, handle source, string skillname) +/** LUA entity:damage (being) + * entity:damage(int damage, int delta, + * int accuracy, int type, int element) + * entity:damage(int damage, int delta, int accuracy, + * int type, int element, handle source) + * entity:damage(int damage, int delta, int accuracy, + * int type, int element, handle source, int skill) + * entity:damage(int damage, int delta, int accuracy, + * int type, int element, handle source, string skillname) ** - * Inflicts damage to ''being''. The severity of the attack is between - * ''damage'' and (''damage'' + ''delta'') and is calculated using the normal [[damage calculation]] rules. The being has a chance to [[hitting and dodging|dodge the attack]] with its [[attributes|agility attribute]]. The ''accuracy'' decides how hard this is. If ''source'' is provided the attack is handled as if the ''source'' triggered the damage. If ''skill'' is given the ''owner'' can also recieve xp for the attack. The ''skill'' should be defined in the [[skills.xml|skills.xml]]. If the skill is provided as string (''skillname'') you have to use this format: <setname>_<skillname>. So for example: "Weapons_Unarmed" + * Valid only for being entities. + * + * Inflicts damage to the being. The severity of the attack is between + * ''damage'' and (''damage'' + ''delta'') and is calculated using the normal + * [[damage calculation]] rules. The being has a chance to + * [[hitting and dodging|dodge the attack]] with its + * [[attributes|agility attribute]]. The ''accuracy'' decides how hard this is. + * + * If ''source'' is provided the attack is handled as if the ''source'' + * triggered the damage. + * + * If ''skill'' is given the ''owner'' can also recieve XP for the attack. The + * ''skill'' should be defined in the [[skills.xml|skills.xml]]. If the skill + * is provided as string (''skillname'') you have to use this format: + * <setname>_<skillname>. So for example: "Weapons_Unarmed" * * ''type'' affects which kind of armor and character attributes reduce the * damage. It can be one of the following values: @@ -1376,13 +1461,13 @@ static int being_walk(lua_State *s) * * **Return Value**: Actual HP reduction resulting from the attack. */ -static int being_damage(lua_State *s) +static int entity_damage(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); if (!being->canFight()) { - luaL_error(s, "being_damage called with victim that cannot fight"); + luaL_error(s, "damage called with victim that cannot fight"); return 0; } @@ -1392,14 +1477,14 @@ static int being_damage(lua_State *s) dmg.cth = luaL_checkint(s, 4); dmg.type = (DamageType)luaL_checkint(s, 5); dmg.element = (Element)luaL_checkint(s, 6); - Being *source = 0; + Entity *source = 0; if (lua_gettop(s) >= 7) { source = checkBeing(s, 7); if (!source->canFight()) { - luaL_error(s, "being_damage called with source that cannot fight"); + luaL_error(s, "damage called with source that cannot fight"); return 0; } } @@ -1407,52 +1492,52 @@ static int being_damage(lua_State *s) { dmg.skill = checkSkill(s, 8); } - being->damage(source, dmg); + being->getComponent<CombatComponent>()->damage(*being, source, dmg); return 0; } -/** LUA being_heal (being) - * being_heal(handle being[, int value]) +/** LUA entity:heal (being) + * entity:heal([int value]) ** - * Restores ''value'' lost hit points to ''being''. Value can be omitted to + * Valid only for being entities. + * + * Restores ''value'' lost hit points to the being. Value can be omitted to * restore the being to full hit points. * * While you can (ab)use this function to hurt a being by using a negative - * value you should rather use being_damage for this purpose. + * value you should rather use entity:damage for this purpose. */ -static int being_heal(lua_State *s) +static int entity_heal(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); if (lua_gettop(s) == 1) // when there is only one argument - { - being->heal(); - } + being->getComponent<BeingComponent>()->heal(*being); else - { - being->heal(luaL_checkint(s, 2)); - } + being->getComponent<BeingComponent>()->heal(*being, luaL_checkint(s, 2)); return 0; } -/** LUA being_get_name (being) - * being_get_name(handle being) +/** LUA entity:name (being) + * entity:name() ** + * Valid only for being entities. + * * **Return value:** Name of the being. */ -static int being_get_name(lua_State *s) +static int entity_get_name(lua_State *s) { - Being *being = checkBeing(s, 1); - push(s, being->getName()); + Entity *being = checkBeing(s, 1); + push(s, being->getComponent<BeingComponent>()->getName()); return 1; } -/** LUA being_type (being) - * being_type(handle being) +/** LUA entity:type (being) + * entity:type() ** - * **Return value:** Type of the given being. These type constants are defined + * **Return value:** Type of the given entity. These type constants are defined * in libmana-constants.lua: * * | 0 | TYPE_ITEM | @@ -1463,18 +1548,20 @@ static int being_get_name(lua_State *s) * | 5 | TYPE_EFFECT | * | 6 | TYPE_OTHER | */ -static int being_type(lua_State *s) +static int entity_get_type(lua_State *s) { - Being *being = checkBeing(s, 1); - lua_pushinteger(s, being->getType()); + Entity *entity = LuaEntity::check(s, 1); + lua_pushinteger(s, entity->getType()); return 1; } -/** LUA being_get_action (being) - * being_get_action(handle being) +/** LUA entity:action (being) + * entity:action() ** - * **Return value:** Current action of the given being. These action constants - * are defined in libmana-constants.lua: + * Valid only for being entities. + * + * **Return value:** Current action of the being. These action constants are + * defined in libmana-constants.lua: * * | 0 | ACTION_STAND | * | 1 | ACTION_WALK | @@ -1483,31 +1570,35 @@ static int being_type(lua_State *s) * | 4 | ACTION_DEAD | * | 5 | ACTION_HURT | */ -static int being_get_action(lua_State *s) +static int entity_get_action(lua_State *s) { - Being *being = checkBeing(s, 1); - lua_pushinteger(s, being->getAction()); + Entity *being = checkBeing(s, 1); + lua_pushinteger(s, being->getComponent<BeingComponent>()->getAction()); return 1; } -/** LUA being_set_action (being) - * being_set_action(handle being, int action) +/** LUA entity:set_action (being) + * entity:set_action(int action) ** + * Valid only for being entities. + * * Sets the current action for the being. */ -static int being_set_action(lua_State *s) +static int entity_set_action(lua_State *s) { - Being *being = checkBeing(s, 1); - int act = luaL_checkint(s, 2); - being->setAction((BeingAction) act); + Entity *being = checkBeing(s, 1); + BeingAction act = static_cast<BeingAction>(luaL_checkint(s, 2)); + being->getComponent<BeingComponent>()->setAction(*being, act); return 0; } -/** LUA being_get_direction (being) - * being_get_direction(handle being) +/** LUA entity:direction (being) + * entity:direction() ** - * **Return value:** Current direction of the given being. These direction - * constants are defined in libmana-constants.lua: + * Valid only for being entities. + * + * **Return value:** Current direction of the being. These direction constants + * are defined in libmana-constants.lua: * * | 0 | DIRECTION_DEFAULT | * | 1 | DIRECTION_UP | @@ -1516,43 +1607,47 @@ static int being_set_action(lua_State *s) * | 4 | DIRECTION_RIGHT | * | 5 | DIRECTION_INVALID | */ -static int being_get_direction(lua_State *s) +static int entity_get_direction(lua_State *s) { - Being *being = checkBeing(s, 1); - lua_pushinteger(s, being->getDirection()); + Entity *being = checkBeing(s, 1); + lua_pushinteger(s, being->getComponent<BeingComponent>()->getDirection()); return 1; } -/** LUA being_set_direction (being) - * being_set_direction(handle being, int direction) +/** LUA entity:set_direction (being) + * entity:set_direction(int direction) ** + * Valid only for being entities. + * * Sets the current direction of the given being. Directions are same as in - * ''being_get_direction''. + * ''entity:direction''. */ -static int being_set_direction(lua_State *s) +static int entity_set_direction(lua_State *s) { - Being *being = checkBeing(s, 1); - BeingDirection dir = (BeingDirection) luaL_checkint(s, 2); - being->setDirection(dir); + Entity *being = checkBeing(s, 1); + BeingDirection dir = static_cast<BeingDirection>(luaL_checkint(s, 2)); + being->getComponent<BeingComponent>()->setDirection(*being, dir); return 0; } -/** LUA being_set_walkmask (being) - * being_set_walkmask(handle being, string mask) +/** LUA entity:set_walkmask (being) + * entity:set_walkmask(string mask) ** - * Sets the walkmasks of a being. The mask is a set of characters which stand + * Valid only for actor entities. + * + * Sets the walkmasks of an actor. The mask is a set of characters which stand * for different collision types. * * | w | Wall | * | c | Character | * | m | Monster | * - * This means being_set_walkmask(being, "wm") will prevent the being from - * walking over walls and monsters. + * This means entity:set_walkmask("wm") will prevent the being from walking + * over walls and monsters. */ -static int being_set_walkmask(lua_State *s) +static int entity_set_walkmask(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkActor(s, 1); const char *stringMask = luaL_checkstring(s, 2); unsigned char mask = 0x00; if (strchr(stringMask, 'w')) @@ -1561,20 +1656,23 @@ static int being_set_walkmask(lua_State *s) mask |= Map::BLOCKMASK_CHARACTER; else if (strchr(stringMask, 'm')) mask |= Map::BLOCKMASK_MONSTER; - being->setWalkMask(mask); + being->getComponent<ActorComponent>()->setWalkMask(mask); return 0; } -/** LUA being_get_walkmask (being) - * being_get_walkmask(handle being) +/** LUA being_entity:walkmask (being) + * entity:walkmask() ** - * **Return value:** The walkmask of the being formatted as string. (See - * [[scripting#get_item_class|being_set_walkmask]]) + * Valid only for actor entities. + * + * **Return value:** The walkmask of the actor formatted as string. (See + * [[scripting#entityset_walkmask|entity:set_walkmask]]) */ -static int being_get_walkmask(lua_State *s) +static int entity_get_walkmask(lua_State *s) { - Being *being = checkBeing(s, 1); - const unsigned char mask = being->getWalkMask(); + Entity *being = checkBeing(s, 1); + const unsigned char mask = + being->getComponent<ActorComponent>()->getWalkMask(); luaL_Buffer buffer; luaL_buffinit(s, &buffer); if (mask & Map::BLOCKMASK_WALL) @@ -1587,25 +1685,27 @@ static int being_get_walkmask(lua_State *s) return 1; } -/** LUA chr_warp (being) - * chr_warp(handle character, int mapID, int posX, int posY) - * chr_warp(handle character, string mapName, int posX, int posY) +/** LUA entity:warp (being) + * entity:warp(int mapID, int posX, int posY) + * entity:warp(string mapName, int posX, int posY) ** - * Teleports the ''character'' to the position ''posX'':''posY'' on the map + * Valid only for character entities. + * + * Teleports the character to the position ''posX'':''posY'' on the map * with the ID number ''mapID'' or name ''mapName''. The ''mapID'' can be - * substituted by ''nil'' to warp the ''character'' to a new position on the + * substituted by ''nil'' to warp the character to a new position on the * current map. */ -static int chr_warp(lua_State *s) +static int entity_warp(lua_State *s) { - Character *q = checkCharacter(s, 1); + Entity *character = checkCharacter(s, 1); int x = luaL_checkint(s, 3); int y = luaL_checkint(s, 4); bool b = lua_isnil(s, 2); if (!(b || lua_isnumber(s, 2) || lua_isstring(s, 2))) { - luaL_error(s, "chr_warp called with incorrect parameters."); + luaL_error(s, "warp called with incorrect parameters."); return 0; } MapComposite *m; @@ -1630,7 +1730,7 @@ static int chr_warp(lua_State *s) if (!map->getWalk(x / map->getTileWidth(), y / map->getTileHeight())) { int c = 50; - LOG_INFO("chr_warp called with a non-walkable place."); + LOG_INFO("warp called with a non-walkable place."); do { x = rand() % map->getWidth(); @@ -1639,80 +1739,108 @@ static int chr_warp(lua_State *s) x *= map->getTileWidth(); y *= map->getTileHeight(); } - GameState::enqueueWarp(q, m, x, y); + GameState::enqueueWarp(character, m, Point(x, y)); return 0; } -/** LUA posX (being) - * posX(handle being) +/** LUA entity:position (being) + * entity:position() + ** + * Valid only for actor entities. + * + * **Return value:** The x and y position of the actor in pixels, measured from + * the top-left corner of the map it is currently on. + */ +static int entity_get_position(lua_State *s) +{ + Entity *being = checkActor(s, 1); + const Point &p = being->getComponent<ActorComponent>()->getPosition(); + lua_pushinteger(s, p.x); + lua_pushinteger(s, p.y); + return 2; +} + +/** LUA entity:x (being) + * entity:x() ** - * **Return value:** The horizontal position of the ''being'' in pixels - * measured from the left border of the map it is currently on. + * Valid only for actor entities. + * + * **Return value:** The x position of the actor in pixels, measured from + * the left border of the map it is currently on. */ -static int posX(lua_State *s) +static int entity_get_x(lua_State *s) { - Being *being = checkBeing(s, 1); - lua_pushinteger(s, being->getPosition().x); + Entity *being = checkActor(s, 1); + const Point &p = being->getComponent<ActorComponent>()->getPosition(); + lua_pushinteger(s, p.x); return 1; } -/** LUA posY (being) - * posY(handle being) +/** LUA entity:y (being) + * entity:y() ** - * **Return value:** The vertical position of the ''being'' in pixels measured - * from the upper border of the map it is currently on. + * Valid only for actor entities. + * + * **Return value:** The y position of the actor in pixels, measured from + * the top border of the map it is currently on. */ -static int posY(lua_State *s) +static int entity_get_y(lua_State *s) { - Being *being = checkBeing(s, 1); - lua_pushinteger(s, being->getPosition().y); + Entity *being = checkActor(s, 1); + const Point &p = being->getComponent<ActorComponent>()->getPosition(); + lua_pushinteger(s, p.y); return 1; } -/** LUA being_get_base_attribute (being) - * being_get_base_attribute(handle being, int attribute_id) +/** LUA entity:base_attribute (being) + * entity:base_attribute(int attribute_id) ** - * Set the value of the being's ''base attribute'' to the 'new_value' parameter - * given. (It can be negative). + * Valid only for being entities. + * + * **Return value:** Returns the value of the being's ''base attribute''. */ -static int being_get_base_attribute(lua_State *s) +static int entity_get_base_attribute(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); int attr = luaL_checkint(s, 2); luaL_argcheck(s, attr > 0, 2, "invalid attribute id"); - lua_pushinteger(s, being->getAttribute(attr)); + lua_pushinteger(s, being->getComponent<BeingComponent>()->getAttributeBase(attr)); return 1; } -/** LUA being_set_base_attribute (being) - * being_set_base_attribute(handle being, int attribute_id, double new_value) +/** LUA entity:set_base_attribute (being) + * entity:set_base_attribute(int attribute_id, double new_value) ** - * **Return value:** Returns the double value of the being's ''base attribute''. + * Valid only for being entities. + * + * Set the value of the being's ''base attribute'' to the 'new_value' parameter + * given. (It can be negative). */ -static int being_set_base_attribute(lua_State *s) +static int entity_set_base_attribute(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); int attr = luaL_checkint(s, 2); double value = luaL_checknumber(s, 3); - being->setAttribute(attr, value); + being->getComponent<BeingComponent>()->setAttribute(*being, attr, value); return 0; } -/** being_get_modified_attribute (being) - * being_get_modified_attribute(handle being, int attribute_id) +/** entity:modified_attribute (being) + * entity:modified_attribute(int attribute_id) ** - * *Return value:** Returns the double value of the being's - * ''modified attribute''. + * Valid only for being entities. + * + * *Return value:** Returns the value of the being's ''modified attribute''. * * The modified attribute is equal to the base attribute + currently applied * modifiers. * * To get to know how to configure and create modifiers, you can have a look at - * the [[attributes.xml]] file and at the [[#being_apply_attribute_modifier]]() - * and [[#being_remove_attribute_modifier]]() lua functions. + * the [[attributes.xml]] file and at the [[#entityapply_attribute_modifier]]() + * and [[#entityremove_attribute_modifier]]() lua functions. * * Note also that items, equipment, and monsters attacks can cause attribute * modifiers. @@ -1720,21 +1848,25 @@ static int being_set_base_attribute(lua_State *s) * FIXME: This functions about applying and removing modifiers are still WIP, * because some simplifications and renaming could occur. */ -static int being_get_modified_attribute(lua_State *s) +static int entity_get_modified_attribute(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); int attr = luaL_checkint(s, 2); luaL_argcheck(s, attr > 0, 2, "invalid attribute id"); - lua_pushinteger(s, being->getModifiedAttribute(attr)); + const double value = + being->getComponent<BeingComponent>()->getModifiedAttribute(attr); + lua_pushinteger(s, value); return 1; } -/** LUA being_apply_attribute_modifier (being) - * being_apply_attribute_modifier(handle being, int attribute_id, double value, - * unsigned int layer, [unsigned short duration, - * [unsigned int effect_id]]) +/** LUA entity:apply_attribute_modifier (being) + * entity:apply_attribute_modifier(int attribute_id, double value, + * unsigned int layer, [unsigned short duration, + * [unsigned int effect_id]]) ** + * Valid only for being entities. + * * **Parameters description:** \\ * * **value** (double): The modifier value (can be negative). * * **layer** (unsigned int): The layer or level of the modifier. @@ -1746,40 +1878,47 @@ static int being_get_modified_attribute(lua_State *s) * * **effect_id** (unsigned int): Set and keep that parameter when you want * to retrieve the exact layer later. (FIXME: Check this.) */ -static int being_apply_attribute_modifier(lua_State *s) +static int entity_apply_attribute_modifier(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); int attr = luaL_checkint(s,2); double value = luaL_checknumber(s, 3); int layer = luaL_checkint(s, 4); int duration = luaL_optint(s, 5, 0); int effectId = luaL_optint(s, 6, 0); - being->applyModifier(attr, value, layer, duration, effectId); + being->getComponent<BeingComponent>()->applyModifier(*being, attr, value, + layer, duration, + effectId); return 0; } -/** LUA being_remove_attribute_modifier (being) - * being_remove_attribute_modifier(handle being, int attribute_id, - * double value, unsigned int layer) +/** LUA entity:remove_attribute_modifier (being) + * entity:remove_attribute_modifier(int attribute_id, + * double value, unsigned int layer) ** + * Valid only for being entities. + * * Permits to remove an attribute modifier by giving its value and its layer. */ -static int being_remove_attribute_modifier(lua_State *s) +static int entity_remove_attribute_modifier(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); int attr = luaL_checkint(s, 2); double value = luaL_checknumber(s, 3); int layer = luaL_checkint(s, 4); int effectId = luaL_optint(s, 5, 0); - being->removeModifier(attr, value, layer, effectId); + being->getComponent<BeingComponent>()->removeModifier(*being, attr, value, + layer, effectId); return 0; } -/** LUA being_get_gender (being) - * being_get_gender(handle being) +/** LUA entity:gender (being) + * entity:gender() ** + * Valid only for being entities. + * * **Return value:** The gender of the being. These gender constants are * defined in libmana-constants.lua: * @@ -1787,17 +1926,19 @@ static int being_remove_attribute_modifier(lua_State *s) * | 1 | GENDER_FEMALE | * | 2 | GENDER_UNSPECIFIED | */ -static int being_get_gender(lua_State *s) +static int entity_get_gender(lua_State *s) { - Being *b = checkBeing(s, 1); - lua_pushinteger(s, b->getGender()); + Entity *b = checkBeing(s, 1); + lua_pushinteger(s, b->getComponent<BeingComponent>()->getGender()); return 1; } -/** LUA being_set_gender (being) - * being_set_gender(handle being, int gender) +/** LUA entity:set_gender (being) + * entity:set_gender(int gender) ** - * Sets the gender of a ''being''. + * Valid only for being entities. + * + * Sets the gender of the being. * * The gender constants are defined in libmana-constants.lua: * @@ -1805,206 +1946,232 @@ static int being_get_gender(lua_State *s) * | 1 | GENDER_FEMALE | * | 2 | GENDER_UNSPECIFIED | */ -static int being_set_gender(lua_State *s) +static int entity_set_gender(lua_State *s) { - Being *b = checkBeing(s, 1); + Entity *b = checkBeing(s, 1); const int gender = luaL_checkinteger(s, 2); - b->setGender(getGender(gender)); + b->getComponent<BeingComponent>()->setGender(getGender(gender)); return 0; } -/** LUA chr_get_level (being) - * chr_get_level(handle character) - * chr_get_level(handle character, int skill_id) - * chr_get_level(handle character, string skill_name) +/** LUA entity:level (being) + * entity:level() + * entity:level(int skill_id) + * entity:level(string skill_name) ** - * **Return value:** Returns the level of the ''character''. If a skill is - * passed (either by name or id) the level of this skill is returned. + * Valid only for character entities. + * + * **Return value:** Returns the level of the character. If a skill is passed + * (either by name or id) the level of this skill is returned. * * **Note:** If the skill is provided as string (''skill_name'') you have to * use this format: <setname>_<skillname>. So for example: "Weapons_Unarmed". */ -static int chr_get_level(lua_State *s) +static int entity_get_level(lua_State *s) { - Character *ch = checkCharacter(s, 1); + Entity *ch = checkCharacter(s, 1); + auto *characterComponent = ch->getComponent<CharacterComponent>(); if (lua_gettop(s) > 1) { int skillId = checkSkill(s, 2); - lua_pushinteger(s, ch->levelForExp(ch->getExperience(skillId))); + lua_pushinteger(s, characterComponent->levelForExp( + characterComponent->getExperience(skillId))); } else { - lua_pushinteger(s, ch->getLevel()); + lua_pushinteger(s, characterComponent->getLevel()); } return 1; } -/** LUA chr_get_exp (being) - * chr_get_exp(handle character, int skill) - * chr_get_exp(handle character, int skill) +/** LUA entity:xp (being) + * entity:xp(int skill_id) + * entity:xp(string skill_name) ** - * **Return value:** The total experience collected by ''character'' in skill ''skill''. + * Valid only for character entities. + * + * **Return value:** The total experience collected by the character in + * ''skill''. * * If the skill is provided as string (''skillname'') you have to use this * format: <setname>_<skillname>. So for example: "Weapons_Unarmed". */ -static int chr_get_exp(lua_State *s) +static int entity_get_xp(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); int skill = checkSkill(s, 2); - const int exp = c->getExperience(skill); + const int exp = c->getComponent<CharacterComponent>()->getExperience(skill); lua_pushinteger(s, exp); return 1; } -/** LUA chr_give_exp (being) - * chr_give_exp(handle character, int skill, int amount [, int optimalLevel]) - * chr_give_exp(handle character, string skillname, int amount [, int optimalLevel]) +/** LUA entity:give_xp (being) + * entity:give_xp(int skill, int amount [, int optimalLevel]) + * entity:give_xp(string skillname, int amount [, int optimalLevel]) ** - * Gives ''character'' ''amount'' experience in skill ''skill''. When an + * Valid only for character entities. + * + * Gives the character ''amount'' experience in skill ''skill''. When an * optimal level is set (over 0), the experience is reduced when the characters * skill level is beyond this. If the skill is provided as string * (''skillname'') you have to use this format: <setname>_<skillname>. * So for example: "Weapons_Unarmed". */ -static int chr_give_exp(lua_State *s) +static int entity_give_xp(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); int skill = checkSkill(s, 2); const int exp = luaL_checkint(s, 3); const int optimalLevel = luaL_optint(s, 4, 0); - c->receiveExperience(skill, exp, optimalLevel); + c->getComponent<CharacterComponent>()->receiveExperience(skill, exp, + optimalLevel); return 0; } -/** LUA exp_for_level (being) - * exp_for_level(int level) +/** LUA xp_for_level (being) + * xp_for_level(int level) ** * **Return value:** Returns the total experience necessary (counted from * level 0) for reaching ''level'' in any skill. */ -static int exp_for_level(lua_State *s) +static int xp_for_level(lua_State *s) { const int level = luaL_checkint(s, 1); - lua_pushinteger(s, Character::expForLevel(level)); + lua_pushinteger(s, CharacterComponent::expForLevel(level)); return 1; } -/** LUA chr_get_hair_color (being) - * chr_get_hair_color(handle character) +/** LUA entity:hair_color (being) + * entity:hair_color() ** - * **Return value:** The hair color ID of ''character'' + * Valid only for character entities. + * + * **Return value:** The hair color ID of the character. */ -static int chr_get_hair_color(lua_State *s) +static int entity_get_hair_color(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); - lua_pushinteger(s, c->getHairColor()); + lua_pushinteger(s, c->getComponent<CharacterComponent>()->getHairColor()); return 1; } -/** LUA chr_set_hair_color (being) - * chr_set_hair_color(handle character, int color) +/** LUA entity:set_hair_color (being) + * entity:set_hair_color(int color) ** - * Sets the hair color ID of ''character'' to ''color'' + * Valid only for character entities. + * + * Sets the hair color ID of the character to ''color''. */ -static int chr_set_hair_color(lua_State *s) +static int entity_set_hair_color(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); const int color = luaL_checkint(s, 2); luaL_argcheck(s, color >= 0, 2, "invalid color id"); - c->setHairColor(color); - c->raiseUpdateFlags(UPDATEFLAG_LOOKSCHANGE); + c->getComponent<CharacterComponent>()->setHairColor(color); + c->getComponent<ActorComponent>()->raiseUpdateFlags( + UPDATEFLAG_LOOKSCHANGE); return 0; } -/** LUA chr_get_hair_style (being) - * chr_get_hair_style(handle character) +/** LUA entity:hair_style (being) + * entity:hair_style() ** - * **Return value:** The hair style ID of ''character'' + * Valid only for character entities. + * + * **Return value:** The hair style ID of the character. */ -static int chr_get_hair_style(lua_State *s) +static int entity_get_hair_style(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); - lua_pushinteger(s, c->getHairStyle()); + lua_pushinteger(s, c->getComponent<CharacterComponent>()->getHairStyle()); return 1; } -/** LUA chr_set_hair_style (being) - * chr_set_hair_style(handle character, int style) +/** LUA entity:set_hair_style (being) + * entity:set_hair_style(int style) ** - * Sets the hair style ID of ''character'' to ''style'' + * Valid only for character entities. + * + * Sets the hair style ID of the character to ''style''. */ -static int chr_set_hair_style(lua_State *s) +static int entity_set_hair_style(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); const int style = luaL_checkint(s, 2); luaL_argcheck(s, style >= 0, 2, "invalid style id"); - c->setHairStyle(style); - c->raiseUpdateFlags(UPDATEFLAG_LOOKSCHANGE); + c->getComponent<CharacterComponent>()->setHairStyle(style); + c->getComponent<ActorComponent>()->raiseUpdateFlags( + UPDATEFLAG_LOOKSCHANGE); return 0; } -/** LUA chr_get_kill_count (being) - * chr_get_kill_count(handle character, int monsterId) - * chr_get_kill_count(handle character, string monsterName) - * chr_get_kill_count(handle character, MonsterClass monsterClass) +/** LUA entity:kill_count (being) + * entity:kill_count(int monsterId) + * entity:kill_count(string monsterName) + * entity:kill_count(MonsterClass monsterClass) ** + * Valid only for character entities. + * * **Return value:** The total number of monsters of the specy (passed either - * as monster id, monster name or monster class) the ''character'' has killed + * as monster id, monster name or monster class) the character has killed * during its career. */ -static int chr_get_kill_count(lua_State *s) +static int entity_get_kill_count(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); MonsterClass *monster = checkMonsterClass(s, 2); - lua_pushinteger(s, c->getKillCount(monster->getId())); + lua_pushinteger(s, c->getComponent<CharacterComponent>()->getKillCount(monster->getId())); return 1; } -/** LUA chr_get_rights (being) - * chr_get_rights(handle character) +/** LUA entity:rights (being) + * entity:rights() ** - * **Return value:** The access level of the account of character. + * Valid only for character entities. + * + * **Return value:** The access level of the account of the character. */ -static int chr_get_rights(lua_State *s) +static int entity_get_rights(lua_State *s) { - Character *c = checkCharacter(s, 1); - lua_pushinteger(s, c->getAccountLevel()); + Entity *c = checkCharacter(s, 1); + lua_pushinteger(s, c->getComponent<CharacterComponent>()->getAccountLevel()); return 1; } -/** LUA chr_kick (being) - * chr_kick(handle character) +/** LUA entity:kick (being) + * entity:kick() ** + * Valid only for character entities. + * * Kicks the character. */ -static int chr_kick(lua_State *s) +static int entity_kick(lua_State *s) { - Character *ch = checkCharacter(s, 1); + Entity *ch = checkCharacter(s, 1); MessageOut kickmsg(GPMSG_CONNECT_RESPONSE); kickmsg.writeInt8(ERRMSG_ADMINISTRATIVE_LOGOFF); - ch->getClient()->disconnect(kickmsg); + ch->getComponent<CharacterComponent>()->getClient()->disconnect(kickmsg); return 0; } -/** LUA being_get_mapid (being) - * being_get_mapid(handle Being) +/** LUA entity:mapid (being) + * entity:mapid() ** - * **Return value:** the id of the map where the being is located - * or nil if there is none. + * **Return value:** the id of the map where the entity is located or nil if + * there is none. */ -static int being_get_mapid(lua_State *s) +static int entity_get_mapid(lua_State *s) { - Being *being = checkBeing(s, 1); - if (MapComposite *map = being->getMap()) + Entity *entity = LuaEntity::check(s, 1); + if (MapComposite *map = entity->getMap()) lua_pushinteger(s, map->getID()); else lua_pushnil(s); @@ -2021,7 +2188,7 @@ static int being_get_mapid(lua_State *s) */ static int chr_request_quest(lua_State *s) { - Character *ch = checkCharacter(s, 1); + Entity *ch = checkCharacter(s, 1); const char *name = luaL_checkstring(s, 2); luaL_argcheck(s, name[0] != 0, 2, "empty variable name"); luaL_checktype(s, 3, LUA_TFUNCTION); @@ -2060,7 +2227,7 @@ static int chr_request_quest(lua_State *s) */ static int chr_try_get_quest(lua_State *s) { - Character *q = checkCharacter(s, 1); + Entity *q = checkCharacter(s, 1); const char *name = luaL_checkstring(s, 2); luaL_argcheck(s, name[0] != 0, 2, "empty variable name"); @@ -2078,18 +2245,12 @@ static int chr_try_get_quest(lua_State *s) ** * Tries to find an online character by name. * - * **Return value** the character handle or nil if there is none + * **Return value** the character handle or nil if there is none. */ static int get_character_by_name(lua_State *s) { const char *name = luaL_checkstring(s, 1); - - Character *ch = gameHandler->getCharacterByNameSlow(name); - if (!ch) - lua_pushnil(s); - else - lua_pushlightuserdata(s, ch); - + push(s, gameHandler->getCharacterByNameSlow(name)); return 1; } @@ -2100,7 +2261,7 @@ static int get_character_by_name(lua_State *s) */ static int chr_get_post(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); Script *script = getScript(s); Script::Thread *thread = checkCurrentThread(s, script); @@ -2112,34 +2273,38 @@ static int chr_get_post(lua_State *s) return lua_yield(s, 0); } -/** LUA being_register (being) - * being_register(handle being) +/** LUA entity:register (being) + * entity:register() ** - * Makes the server call the on_being_death and on_being_remove callbacks - * when the being dies or is removed from the map. + * Makes the server call the on_being_death and on_entity_remove callbacks + * when the being dies or the entity is removed from the map. * * **Note:** You should never need to call this in most situations. It is * handeled by the libmana.lua */ -static int being_register(lua_State *s) +static int entity_register(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *entity = LuaEntity::check(s, 1); Script *script = getScript(s); - being->signal_died.connect(sigc::mem_fun(script, &Script::processDeathEvent)); - being->signal_removed.connect(sigc::mem_fun(script, &Script::processRemoveEvent)); + entity->signal_removed.connect(sigc::mem_fun(script, &Script::processRemoveEvent)); + + if (BeingComponent *bc = entity->findComponent<BeingComponent>()) + bc->signal_died.connect(sigc::mem_fun(script, &Script::processDeathEvent)); return 0; } -/** LUA chr_shake_screen (being) - * chr_shake_screen(handle character, int x, int y[, float strength, int radius]) +/** LUA entity:shake_screen (being) + * entity:shake_screen(int x, int y[, float strength, int radius]) ** - * Shake the screen for a given character. + * Valid only for character entities. + * + * Shakes the screen for a given character. */ -static int chr_shake_screen(lua_State *s) +static int entity_shake_screen(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); const int x = luaL_checkint(s, 2); const int y = luaL_checkint(s, 3); @@ -2152,74 +2317,83 @@ static int chr_shake_screen(lua_State *s) if (lua_isnumber(s, 5)) msg.writeInt16(lua_tointeger(s, 5)); - c->getClient()->send(msg); + c->getComponent<CharacterComponent>()->getClient()->send(msg); return 0; } -/** LUA chr_create_text_particle (being) - * chr_create_text_particle(handle character, string text) +/** LUA entity:show_text_particle (being) + * entity:show_text_particle(string text) ** - * Creates a text particle on a client. This effect is only visible for the - * ''character''. + * Valid only for character entities. + * + * Shows a text particle on a client. This effect is only visible for the + * character. */ -static int chr_create_text_particle(lua_State *s) +static int entity_show_text_particle(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); const char *text = luaL_checkstring(s, 2); MessageOut msg(GPMSG_CREATE_TEXT_PARTICLE); msg.writeString(text); - c->getClient()->send(msg); + c->getComponent<CharacterComponent>()->getClient()->send(msg); return 0; } -/** LUA chr_give_special (being) - * chr_give_special(handle character, int special) +/** LUA entity:give_special (being) + * entity:give_special(int special) ** + * Valid only for character entities. + * * Enables a special for a character. */ -static int chr_give_special(lua_State *s) +static int entity_give_special(lua_State *s) { // cost_type is ignored until we have more than one cost type - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); const int special = checkSpecial(s, 2); const int currentMana = luaL_optint(s, 3, 0); - c->giveSpecial(special, currentMana); + c->getComponent<CharacterComponent>()->giveSpecial(special, currentMana); return 0; } -/** LUA chr_has_special (being) - * chr_has_special(handle character, int special) +/** LUA entity:has_special (being) + * entity:has_special(int special) ** - * **Return value:** True if the ''character'' has the special. False otherwise + * Valid only for character entities. + * + * **Return value:** True if the character has the special, false otherwise. */ -static int chr_has_special(lua_State *s) +static int entity_has_special(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); const int special = luaL_checkint(s, 2); - lua_pushboolean(s, c->hasSpecial(special)); + lua_pushboolean(s, c->getComponent<CharacterComponent>()->hasSpecial(special)); return 1; } -/** LUA chr_take_special (being) - * chr_take_special(handle character, int special) +/** LUA entity:take_special (being) + * entity:take_special(int special) ** + * Valid only for character entities. + * * Removes a special from a character. * - * **Return value:** True if removal was successful. False otherwise - * (in case the ''character'' did not have the special) + * **Return value:** True if removal was successful, false otherwise (in case + * the character did not have the special). */ -static int chr_take_special(lua_State *s) +static int entity_take_special(lua_State *s) { - Character *c = checkCharacter(s, 1); + Entity *c = checkCharacter(s, 1); const int special = luaL_checkint(s, 2); - lua_pushboolean(s, c->hasSpecial(special)); - c->takeSpecial(special); + CharacterComponent *cc = c->getComponent<CharacterComponent>(); + lua_pushboolean(s, cc->hasSpecial(special)); + cc->takeSpecial(special); return 1; } @@ -2227,56 +2401,67 @@ static int chr_take_special(lua_State *s) /** LUA_CATEGORY Monster (monster) */ -/** LUA monster_get_id (monster) - * monster_get_id(handle monster) +/** LUA entity:monster_id (monster) + * entity:monster_id() ** - * **Return value:** The id of the specy of the monster handle. + * Valid only for monster entities. + * + * **Return value:** The id of the monster class. */ -static int monster_get_id(lua_State *s) +static int entity_get_monster_id(lua_State *s) { - Monster *monster = checkMonster(s, 1); - lua_pushinteger(s, monster->getSpecy()->getId()); + Entity *monster = checkMonster(s, 1); + MonsterComponent *monsterComponent = monster->getComponent<MonsterComponent>(); + lua_pushinteger(s, monsterComponent->getSpecy()->getId()); return 1; } -/** LUA monster_change_anger (monster) - * monster_change_anger(handle monster, handle being, int anger) +/** LUA entity:change_anger (monster) + * entity:change_anger(handle being, int anger) ** - * Makes the ''monster'' more angry about the ''being'' by adding ''anger'' to - * the being. + * Valid only for monster entities. + * + * Makes the monster more angry about the ''being'' by adding ''anger'' to the + * being. */ -static int monster_change_anger(lua_State *s) +static int entity_change_anger(lua_State *s) { - Monster *monster = checkMonster(s, 1); - Being *being = checkBeing(s, 2); + Entity *monster = checkMonster(s, 1); + Entity *being = checkBeing(s, 2); const int anger = luaL_checkint(s, 3); - monster->changeAnger(being, anger); + monster->getComponent<MonsterComponent>()->changeAnger(being, anger); return 0; } -/** LUA monster_drop_anger (monster) - * monster_drop_anger(handle monster, handle target) +/** LUA entity:drop_anger (monster) + * entity:drop_anger(handle target) ** + * Valid only for monster entities. + * * Will drop all anger against the ''target''. */ -static int monster_drop_anger(lua_State *s) +static int entity_drop_anger(lua_State *s) { - Monster *monster = checkMonster(s, 1); - Being *being = checkBeing(s, 2); - monster->forgetTarget(being); + Entity *monster = checkMonster(s, 1); + Entity *being = checkBeing(s, 2); + monster->getComponent<MonsterComponent>()->forgetTarget(being); return 0; } -/** LUA monster_get_angerlist (monster) - * monster_get_angerlist(handle monster) +/** LUA entity:get_angerlist (monster) + * entity:get_angerlist() ** + * Valid only for monster entities. + * * **Return value:** A table with the beings as key and the anger against them * as values. */ -static int monster_get_angerlist(lua_State *s) +static int entity_get_angerlist(lua_State *s) { - Monster *monster = checkMonster(s, 1); - pushSTLContainer(s, monster->getAngerList()); + Entity *monster = checkMonster(s, 1); + MonsterComponent *monsterComponent = + monster->getComponent<MonsterComponent>(); + pushSTLContainer(s, monsterComponent->getAngerList()); return 1; } @@ -2284,76 +2469,86 @@ static int monster_get_angerlist(lua_State *s) /** LUA_CATEGORY Status effects (statuseffects) */ -/** LUA being_apply_status (statuseffects) - * being_apply_status(handle Being, int status_id, int time) +/** LUA entity:apply_status (statuseffects) + * entity:apply_status(int status_id, int time) ** - * Gives a ''being'' a status effect ''status_id'', status effects don't work - * on NPCs. ''time'' is in game ticks. + * Valid only for being entities. + * + * Gives a being a status effect ''status_id'', status effects don't work on + * NPCs. ''time'' is in game ticks. */ -static int being_apply_status(lua_State *s) +static int entity_apply_status(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); const int id = luaL_checkint(s, 2); const int time = luaL_checkint(s, 3); - being->applyStatusEffect(id, time); + being->getComponent<BeingComponent>()->applyStatusEffect(id, time); return 0; } -/** LUA being_remove_status (statuseffects) - * being_remove_status(handle Being, int status_id) +/** LUA entity:remove_status (statuseffects) + * entity:remove_status(int status_id) ** + * Valid only for being entities. + * * Removes a given status effect from a being. */ -static int being_remove_status(lua_State *s) +static int entity_remove_status(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); const int id = luaL_checkint(s, 2); - being->removeStatusEffect(id); + being->getComponent<BeingComponent>()->removeStatusEffect(id); return 0; } -/** LUA being_has_status (statuseffects) - * being_has_status(handle Being, int status_id) +/** LUA entity:has_status (statuseffects) + * entity:has_status(int status_id) ** - * **Return value:** Bool if the being has a given status effect. + * Valid only for being entities. + * + * **Return value:** True if the being has a given status effect. */ -static int being_has_status(lua_State *s) +static int entity_has_status(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); const int id = luaL_checkint(s, 2); - lua_pushboolean(s, being->hasStatusEffect(id)); + lua_pushboolean(s, being->getComponent<BeingComponent>()->hasStatusEffect(id)); return 1; } -/** LUA being_get_status_time (statuseffects) - * being_get_status_time(handle Being, int status_id) +/** LUA entity:status_time (statuseffects) + * entity:status_time(int status_id) ** + * Valid only for being entities. + * * **Return Value:** Number of ticks remaining on a status effect. */ -static int being_get_status_time(lua_State *s) +static int entity_get_status_time(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); const int id = luaL_checkint(s, 2); - lua_pushinteger(s, being->getStatusEffectTime(id)); + lua_pushinteger(s, being->getComponent<BeingComponent>()->getStatusEffectTime(id)); return 1; } -/** LUA being_set_status_time (statuseffects) - * being_set_status_time(handle Being, int status_id, int time) +/** LUA entity:set_status_time (statuseffects) + * entity:set_status_time(int status_id, int time) ** + * Valid only for being entities. + * * Sets the time on a status effect a target being already has. */ -static int being_set_status_time(lua_State *s) +static int entity_set_status_time(lua_State *s) { - Being *being = checkBeing(s, 1); + Entity *being = checkBeing(s, 1); const int id = luaL_checkint(s, 2); const int time = luaL_checkint(s, 3); - being->setStatusEffectTime(id, time); + being->getComponent<BeingComponent>()->setStatusEffectTime(id, time); return 0; } @@ -2432,46 +2627,6 @@ static int map_get_pvp(lua_State *s) } -/** LUA_CATEGORY General Information (information) - */ - -/** LUA monster_get_name (information) - * get_map_id() - ** - * **Return value:** The ID number of the map the script runs on. - */ -static int monster_get_name(lua_State *s) -{ - const int id = luaL_checkint(s, 1); - MonsterClass *spec = monsterManager->getMonster(id); - if (!spec) - { - luaL_error(s, "monster_get_name called with unknown monster id."); - return 0; - } - push(s, spec->getName()); - return 1; -} - -/** LUA item_get_name (information) - * get_map_property(string key) - ** - * **Return value:** The value of the property ''key'' of the current map. - * The string is empty if the property ''key'' does not exist. - */ -static int item_get_name(lua_State *s) -{ - const int id = luaL_checkint(s, 1); - ItemClass *it = itemManager->getItem(id); - if (!it) - { - luaL_error(s, "item_get_name called with unknown item id."); - return 0; - } - push(s, it->getName()); - return 1; -} - /** LUA_CATEGORY Persistent variables (variables) */ @@ -2625,7 +2780,7 @@ static int log(lua_State *s) /** LUA get_beings_in_circle (area) * get_beings_in_circle(int x, int y, int radius) - * get_beings_in_circle(handle being, int radius) + * get_beings_in_circle(handle actor, int radius) ** * **Return value:** This function returns a lua table of all beings in a * circle of radius (in pixels) ''radius'' centered either at the pixel at @@ -2634,10 +2789,10 @@ static int log(lua_State *s) static int get_beings_in_circle(lua_State *s) { int x, y, r; - if (lua_islightuserdata(s, 1)) + if (lua_isuserdata(s, 1)) { - Being *b = checkBeing(s, 1); - const Point &pos = b->getPosition(); + Entity *b = checkActor(s, 1); + const Point &pos = b->getComponent<ActorComponent>()->getPosition(); x = pos.x; y = pos.y; r = luaL_checkint(s, 2); @@ -2657,14 +2812,16 @@ static int get_beings_in_circle(lua_State *s) int tableIndex = 1; for (BeingIterator i(m->getAroundPointIterator(Point(x, y), r)); i; ++i) { - Being *b = *i; + Entity *b = *i; char t = b->getType(); if (t == OBJECT_NPC || t == OBJECT_CHARACTER || t == OBJECT_MONSTER) { - if (Collision::circleWithCircle(b->getPosition(), b->getSize(), + auto *actorComponent = b->getComponent<ActorComponent>(); + if (Collision::circleWithCircle(actorComponent->getPosition(), + actorComponent->getSize(), Point(x, y), r)) { - lua_pushlightuserdata(s, b); + push(s, b); lua_rawseti(s, tableStackPosition, tableIndex); tableIndex++; } @@ -2677,7 +2834,7 @@ static int get_beings_in_circle(lua_State *s) /** LUA get_beings_in_rectangle (area) * get_beings_in_rectangle(int x, int y, int width, int height) ** - * **Return value:** An table of being objects within the rectangle. + * **Return value:** An table of being entities within the rectangle. * All parameters have to be passed as pixels. */ static int get_beings_in_rectangle(lua_State *s) @@ -2696,12 +2853,12 @@ static int get_beings_in_rectangle(lua_State *s) Rectangle rect = {x, y ,w, h}; for (BeingIterator i(m->getInsideRectangleIterator(rect)); i; ++i) { - Being *b = *i; + Entity *b = *i; char t = b->getType(); if ((t == OBJECT_NPC || t == OBJECT_CHARACTER || t == OBJECT_MONSTER) && - rect.contains(b->getPosition())) + rect.contains(b->getComponent<ActorComponent>()->getPosition())) { - lua_pushlightuserdata(s, b); + push(s, b); lua_rawseti(s, tableStackPosition, tableIndex); tableIndex++; } @@ -2721,13 +2878,13 @@ static int get_distance(lua_State *s) int x1, y1, x2, y2; if (lua_gettop(s) == 2) { - Being *being1 = checkBeing(s, 1); - Being *being2 = checkBeing(s, 2); + Entity *being1 = checkBeing(s, 1); + Entity *being2 = checkBeing(s, 2); - x1 = being1->getPosition().x; - y1 = being1->getPosition().y; - x2 = being2->getPosition().x; - y2 = being2->getPosition().y; + x1 = being1->getComponent<ActorComponent>()->getPosition().x; + y1 = being1->getComponent<ActorComponent>()->getPosition().y; + x2 = being2->getComponent<ActorComponent>()->getPosition().x; + y2 = being2->getComponent<ActorComponent>()->getPosition().y; } else { @@ -2948,9 +3105,9 @@ static int monster_class_on_update(lua_State *s) * * **Example:** <code lua> * local function damage(mob, aggressor, hploss) - * being_say(mob, "I took damage -.- ".. hploss) + * mob:say("I took damage -.- ".. hploss) * if aggressor then - * being_say(mob, "Curse you, ".. being_get_name(aggressor)) + * mob:say("Curse you, ".. aggressor:name()) * end * end * local maggot = get_monster_class("maggot") @@ -2964,6 +3121,18 @@ static int monster_class_on_damage(lua_State *s) return 0; } +/** LUA monsterclass:name (monsterclass) + * monsterclass:name() + ** + * **Return value:** The name of the monster class. + */ +static int monster_class_get_name(lua_State *s) +{ + MonsterClass *monsterClass = LuaMonsterClass::check(s, 1); + push(s, monsterClass->getName()); + return 1; +} + /** LUA monsterclass:attacks (monsterclass) * monsterclass:attacks() ** @@ -3143,7 +3312,7 @@ static int damage_get_cth(lua_State *s) ** * **Return value:** This function returns the element of the attack. * - * **See:** [[scripting#being_damage|being_damage]] for possible values. + * **See:** [[scripting#entitydamage|entity:damage]] for possible values. */ static int damage_get_element(lua_State *s) { @@ -3157,7 +3326,7 @@ static int damage_get_element(lua_State *s) ** * **Return value:** This function returns the type of the attack. * - * **See:** [[scripting#being_damage|being_damage]] for possible values. + * **See:** [[scripting#entitydamage|entity:damage]] for possible values. */ static int damage_get_type(lua_State *s) { @@ -3346,6 +3515,18 @@ static int item_class_on(lua_State *s) return 0; } +/** LUA itemclass:name (itemclass) + * itemclass:name() + ** + * **Return value:** The name of the item class. + */ +static int item_class_get_name(lua_State *s) +{ + ItemClass *itemClass = LuaItemClass::check(s, 1); + push(s, itemClass->getName()); + return 1; +} + /** LUA itemclass:attacks (itemclass) * itemclass:attacks() ** @@ -3370,7 +3551,6 @@ static int item_class_attacks(lua_State *s) */ static int test_tableget(lua_State *s) { - std::list<float> list; std::vector<std::string> svector; std::vector<int> ivector; @@ -3397,9 +3577,8 @@ static int test_tableget(lua_State *s) LOG_INFO("Pushing Integer Vector"); ivector.resize(10); for (int i = 1; i < 10; i++) - { - ivector[i-1] = i * i; - } + ivector[i - 1] = i * i; + pushSTLContainer<int>(s, ivector); LOG_INFO("Pushing String/String Map"); @@ -3416,7 +3595,6 @@ static int test_tableget(lua_State *s) set.insert(10); pushSTLContainer<int>(s, set); - return 5; } @@ -3460,123 +3638,59 @@ LuaScript::LuaScript(): // Put the callback functions in the scripting environment. static luaL_Reg const callbacks[] = { - { "on_update_derived_attribute", &on_update_derived_attribute }, - { "on_recalculate_base_attribute", &on_recalculate_base_attribute }, - { "on_character_death", &on_character_death }, - { "on_character_death_accept", &on_character_death_accept }, - { "on_character_login", &on_character_login }, - { "on_being_death", &on_being_death }, - { "on_being_remove", &on_being_remove }, - { "on_update", &on_update }, - { "on_create_npc_delayed", &on_create_npc_delayed }, - { "on_map_initialize", &on_map_initialize }, - { "on_craft", &on_craft }, - { "on_mapvar_changed", &on_mapvar_changed }, - { "on_worldvar_changed", &on_worldvar_changed }, - { "on_mapupdate", &on_mapupdate }, - { "get_item_class", &get_item_class }, - { "get_monster_class", &get_monster_class }, - { "get_status_effect", &get_status_effect }, - { "npc_create", &npc_create }, - { "npc_message", &npc_message }, - { "npc_choice", &npc_choice }, - { "npc_trade", &npc_trade }, - { "npc_post", &npc_post }, - { "npc_enable", &npc_enable }, - { "npc_disable", &npc_disable }, - { "chr_warp", &chr_warp }, - { "chr_get_inventory", &chr_get_inventory }, - { "chr_inv_change", &chr_inv_change }, - { "chr_inv_count", &chr_inv_count }, - { "chr_get_equipment", &chr_get_equipment }, - { "chr_equip_slot", &chr_equip_slot }, - { "chr_equip_item", &chr_equip_item }, - { "chr_unequip_slot", &chr_unequip_slot }, - { "chr_unequip_item", &chr_unequip_item }, - { "chr_get_level", &chr_get_level }, - { "chr_get_quest", &chr_get_quest }, - { "chr_set_quest", &chr_set_quest }, - { "chr_request_quest", &chr_request_quest }, - { "chr_try_get_quest", &chr_try_get_quest }, - { "getvar_map", &getvar_map }, - { "setvar_map", &setvar_map }, - { "getvar_world", &getvar_world }, - { "setvar_world", &setvar_world }, - { "chr_get_post", &chr_get_post }, - { "chr_get_exp", &chr_get_exp }, - { "chr_give_exp", &chr_give_exp }, - { "chr_get_rights", &chr_get_rights }, - { "chr_set_hair_style", &chr_set_hair_style }, - { "chr_get_hair_style", &chr_get_hair_style }, - { "chr_set_hair_color", &chr_set_hair_color }, - { "chr_get_hair_color", &chr_get_hair_color }, - { "chr_get_kill_count", &chr_get_kill_count }, - { "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 }, - { "monster_get_name", &monster_get_name }, - { "monster_get_id", &monster_get_id }, - { "monster_change_anger", &monster_change_anger }, - { "monster_drop_anger", &monster_drop_anger }, - { "monster_get_angerlist", &monster_get_angerlist }, - { "monster_remove", &monster_remove }, - { "being_apply_status", &being_apply_status }, - { "being_remove_status", &being_remove_status }, - { "being_has_status", &being_has_status }, - { "being_set_status_time", &being_set_status_time }, - { "being_get_status_time", &being_get_status_time }, - { "being_get_gender", &being_get_gender }, - { "being_set_gender", &being_set_gender }, - { "being_type", &being_type }, - { "being_walk", &being_walk }, - { "being_say", &being_say }, - { "being_damage", &being_damage }, - { "being_heal", &being_heal }, - { "being_get_name", &being_get_name }, - { "being_get_action", &being_get_action }, - { "being_set_action", &being_set_action }, - { "being_get_direction", &being_get_direction }, - { "being_set_direction", &being_set_direction }, - { "being_apply_attribute_modifier", &being_apply_attribute_modifier }, - { "being_remove_attribute_modifier", &being_remove_attribute_modifier }, - { "being_set_base_attribute", &being_set_base_attribute }, - { "being_get_modified_attribute", &being_get_modified_attribute }, - { "being_get_base_attribute", &being_get_base_attribute }, - { "being_set_walkmask", &being_set_walkmask }, - { "being_get_walkmask", &being_get_walkmask }, - { "being_get_mapid", &being_get_mapid }, - { "posX", &posX }, - { "posY", &posY }, - { "trigger_create", &trigger_create }, - { "chat_message", &chat_message }, - { "get_beings_in_circle", &get_beings_in_circle }, - { "get_beings_in_rectangle", &get_beings_in_rectangle }, - { "get_character_by_name", &get_character_by_name }, - { "being_register", &being_register }, - { "effect_create", &effect_create }, - { "chr_shake_screen", &chr_shake_screen }, - { "chr_create_text_particle", &chr_create_text_particle }, - { "test_tableget", &test_tableget }, - { "get_map_id", &get_map_id }, - { "get_map_property", &get_map_property }, - { "is_walkable", &is_walkable }, - { "map_get_pvp", &map_get_pvp }, - { "item_drop", &item_drop }, - { "item_get_name", &item_get_name }, - { "npc_ask_integer", &npc_ask_integer }, - { "npc_ask_string", &npc_ask_string }, - { "log", &log }, - { "get_distance", &get_distance }, - { "map_get_objects", &map_get_objects }, - { "announce", &announce }, - { "get_special_info", &get_special_info }, + { "on_update_derived_attribute", on_update_derived_attribute }, + { "on_recalculate_base_attribute", on_recalculate_base_attribute }, + { "on_character_death", on_character_death }, + { "on_character_death_accept", on_character_death_accept }, + { "on_character_login", on_character_login }, + { "on_being_death", on_being_death }, + { "on_entity_remove", on_entity_remove }, + { "on_update", on_update }, + { "on_create_npc_delayed", on_create_npc_delayed }, + { "on_map_initialize", on_map_initialize }, + { "on_craft", on_craft }, + { "on_mapvar_changed", on_mapvar_changed }, + { "on_worldvar_changed", on_worldvar_changed }, + { "on_mapupdate", on_mapupdate }, + { "get_item_class", get_item_class }, + { "get_monster_class", get_monster_class }, + { "get_status_effect", get_status_effect }, + { "npc_create", npc_create }, + { "say", say }, + { "ask", ask }, + { "ask_number", ask_number }, + { "ask_string", ask_string }, + { "trade", trade }, + { "npc_post", npc_post }, + { "npc_enable", npc_enable }, + { "npc_disable", npc_disable }, + { "chr_get_quest", chr_get_quest }, + { "chr_set_quest", chr_set_quest }, + { "chr_request_quest", chr_request_quest }, + { "chr_try_get_quest", chr_try_get_quest }, + { "getvar_map", getvar_map }, + { "setvar_map", setvar_map }, + { "getvar_world", getvar_world }, + { "setvar_world", setvar_world }, + { "chr_get_post", chr_get_post }, + { "xp_for_level", xp_for_level }, + { "monster_create", monster_create }, + { "trigger_create", trigger_create }, + { "get_beings_in_circle", get_beings_in_circle }, + { "get_beings_in_rectangle", get_beings_in_rectangle }, + { "get_character_by_name", get_character_by_name }, + { "effect_create", effect_create }, + { "test_tableget", test_tableget }, + { "get_map_id", get_map_id }, + { "get_map_property", get_map_property }, + { "is_walkable", is_walkable }, + { "map_get_pvp", map_get_pvp }, + { "item_drop", item_drop }, + { "log", log }, + { "get_distance", get_distance }, + { "map_get_objects", map_get_objects }, + { "announce", announce }, + { "get_special_info", get_special_info }, { NULL, NULL } }; #if LUA_VERSION_NUM < 502 @@ -3589,66 +3703,136 @@ LuaScript::LuaScript(): lua_pop(mRootState, 1); // pop the globals table static luaL_Reg const members_AttackInfo[] = { - { "priority", &attack_get_priority }, - { "cooldowntime", &attack_get_cooldowntime }, - { "warmuptime", &attack_get_warmuptime }, - { "reusetime", &attack_get_reusetime }, - { "damage", &attack_get_damage }, - { "on_attack", &attack_on_attack }, + { "priority", attack_get_priority }, + { "cooldowntime", attack_get_cooldowntime }, + { "warmuptime", attack_get_warmuptime }, + { "reusetime", attack_get_reusetime }, + { "damage", attack_get_damage }, + { "on_attack", attack_on_attack }, { NULL, NULL } }; static luaL_Reg const members_Damage[] = { - { "id", &damage_get_id }, - { "skill", &damage_get_skill }, - { "base", &damage_get_base }, - { "delta", &damage_get_delta }, - { "cth", &damage_get_cth }, - { "element", &damage_get_element }, - { "type", &damage_get_type }, - { "is_truestrike", &damage_is_truestrike }, - { "range", &damage_get_range }, + { "id", damage_get_id }, + { "skill", damage_get_skill }, + { "base", damage_get_base }, + { "delta", damage_get_delta }, + { "cth", damage_get_cth }, + { "element", damage_get_element }, + { "type", damage_get_type }, + { "is_truestrike", damage_is_truestrike }, + { "range", damage_get_range }, + { NULL, NULL } + }; + + static luaL_Reg const members_Entity[] = { + { "remove", entity_remove }, + { "say", entity_say }, + { "message", entity_message }, + { "inventory", entity_get_inventory }, + { "inv_change", entity_inv_change }, + { "inv_count", entity_inv_count }, + { "equipment", entity_get_equipment }, + { "equip_slot", entity_equip_slot }, + { "equip_item", entity_equip_item }, + { "unequip_slot", entity_unequip_slot }, + { "unequip_item", entity_unequip_item }, + { "set_special_recharge_speed", entity_set_special_recharge_speed }, + { "special_recharge_speed", entity_get_special_recharge_speed }, + { "set_special_mana", entity_set_special_mana }, + { "special_mana", entity_get_special_mana }, + { "walk", entity_walk }, + { "damage", entity_damage }, + { "heal", entity_heal }, + { "name", entity_get_name }, + { "type", entity_get_type }, + { "action", entity_get_action }, + { "set_action", entity_set_action }, + { "direction", entity_get_direction }, + { "set_direction", entity_set_direction }, + { "set_walkmask", entity_set_walkmask }, + { "walkmask", entity_get_walkmask }, + { "warp", entity_warp }, + { "position", entity_get_position }, + { "x", entity_get_x }, + { "y", entity_get_y }, + { "base_attribute", entity_get_base_attribute }, + { "set_base_attribute", entity_set_base_attribute }, + { "modified_attribute", entity_get_modified_attribute }, + { "apply_attribute_modifier", entity_apply_attribute_modifier }, + { "remove_attribute_modifier", entity_remove_attribute_modifier }, + { "gender", entity_get_gender }, + { "set_gender", entity_set_gender }, + { "level", entity_get_level }, + { "xp", entity_get_xp }, + { "give_xp", entity_give_xp }, + { "hair_color", entity_get_hair_color }, + { "set_hair_color", entity_set_hair_color }, + { "hair_style", entity_get_hair_style }, + { "set_hair_style", entity_set_hair_style }, + { "kill_count", entity_get_kill_count }, + { "rights", entity_get_rights }, + { "kick", entity_kick }, + { "mapid", entity_get_mapid }, + { "register", entity_register }, + { "shake_screen", entity_shake_screen }, + { "show_text_particle", entity_show_text_particle }, + { "give_special", entity_give_special }, + { "has_special", entity_has_special }, + { "take_special", entity_take_special }, + { "monster_id", entity_get_monster_id }, + { "change_anger", entity_change_anger }, + { "drop_anger", entity_drop_anger }, + { "angerlist", entity_get_angerlist }, + { "apply_status", entity_apply_status }, + { "remove_status", entity_remove_status }, + { "has_status", entity_has_status }, + { "status_time", entity_get_status_time }, + { "set_status_time", entity_set_status_time }, { NULL, NULL } }; static luaL_Reg const members_ItemClass[] = { - { "on", &item_class_on }, - { "attacks", &item_class_attacks }, + { "on", item_class_on }, + { "name", item_class_get_name }, + { "attacks", item_class_attacks }, { NULL, NULL } }; static luaL_Reg const members_MapObject[] = { - { "property", &map_object_get_property }, - { "bounds", &map_object_get_bounds }, - { "name", &map_object_get_name }, - { "type", &map_object_get_type }, + { "property", map_object_get_property }, + { "bounds", map_object_get_bounds }, + { "name", map_object_get_name }, + { "type", map_object_get_type }, { NULL, NULL } }; static luaL_Reg const members_MonsterClass[] = { - { "on_update", &monster_class_on_update }, - { "on_damage", &monster_class_on_damage }, - { "attacks", &monster_class_attacks }, + { "on_update", monster_class_on_update }, + { "on_damage", monster_class_on_damage }, + { "name", monster_class_get_name }, + { "attacks", monster_class_attacks }, { NULL, NULL } }; static luaL_Reg const members_StatusEffect[] = { - { "on_tick", &status_effect_on_tick }, + { "on_tick", status_effect_on_tick }, { 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 }, + { "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} }; LuaAttackInfo::registerType(mRootState, "Attack", members_AttackInfo); LuaDamage::registerType(mRootState, "Damage", members_Damage); + LuaEntity::registerType(mRootState, "Entity", members_Entity); LuaItemClass::registerType(mRootState, "ItemClass", members_ItemClass); LuaMapObject::registerType(mRootState, "MapObject", members_MapObject); LuaMonsterClass::registerType(mRootState, "MonsterClass", members_MonsterClass); diff --git a/src/scripting/luascript.cpp b/src/scripting/luascript.cpp index e9f4492d..f5fb6a4e 100644 --- a/src/scripting/luascript.cpp +++ b/src/scripting/luascript.cpp @@ -75,24 +75,21 @@ void LuaScript::prepareResume(Thread *thread) void LuaScript::push(int v) { assert(nbArgs >= 0); - lua_pushinteger(mCurrentState, v); + ::push(mCurrentState, v); ++nbArgs; } void LuaScript::push(const std::string &v) { assert(nbArgs >= 0); - lua_pushlstring(mCurrentState, v.c_str(), v.length()); + ::push(mCurrentState, v); ++nbArgs; } void LuaScript::push(Entity *v) { assert(nbArgs >= 0); - if (v) - lua_pushlightuserdata(mCurrentState, v); - else - lua_pushnil(mCurrentState); + ::push(mCurrentState, v); ++nbArgs; } @@ -248,7 +245,7 @@ void LuaScript::load(const char *prog, const char *name, mContext = previousContext; } -void LuaScript::processDeathEvent(Being *entity) +void LuaScript::processDeathEvent(Entity *entity) { if (mDeathNotificationCallback.isValid()) { @@ -275,35 +272,37 @@ void LuaScript::processRemoveEvent(Entity *entity) /** * Called when the server has recovered the value of a quest variable. */ -void LuaScript::getQuestCallback(Character *q, +void LuaScript::getQuestCallback(Entity *q, const std::string &value, Script *script) { - Script::Thread *thread = q->getNpcThread(); + auto *characterComponent = q->getComponent<CharacterComponent>(); + Script::Thread *thread = characterComponent->getNpcThread(); if (!thread || thread->mState != Script::ThreadExpectingString) return; script->prepareResume(thread); script->push(value); - q->resumeNpcThread(); + characterComponent->resumeNpcThread(); } /** * Called when the server has recovered the post for a user. */ -void LuaScript::getPostCallback(Character *q, +void LuaScript::getPostCallback(Entity *q, const std::string &sender, const std::string &letter, Script *script) { - Script::Thread *thread = q->getNpcThread(); + auto *characterComponent = q->getComponent<CharacterComponent>(); + Script::Thread *thread = characterComponent->getNpcThread(); if (!thread || thread->mState != Script::ThreadExpectingTwoStrings) return; script->prepareResume(thread); script->push(sender); script->push(letter); - q->resumeNpcThread(); + characterComponent->resumeNpcThread(); } diff --git a/src/scripting/luascript.h b/src/scripting/luascript.h index 3bedbc28..b874976a 100644 --- a/src/scripting/luascript.h +++ b/src/scripting/luascript.h @@ -28,7 +28,7 @@ extern "C" { #include "scripting/script.h" -class Character; +class CharacterComponent; /** * Implementation of the Script class for Lua. @@ -69,16 +69,16 @@ class LuaScript : public Script void unref(Ref &ref); - static void getQuestCallback(Character *, + static void getQuestCallback(Entity *, const std::string &value, Script *); - static void getPostCallback(Character *, + static void getPostCallback(Entity *, const std::string &sender, const std::string &letter, Script *); - void processDeathEvent(Being *entity); + void processDeathEvent(Entity *entity); void processRemoveEvent(Entity *entity); diff --git a/src/scripting/luautil.cpp b/src/scripting/luautil.cpp index 6210eda0..ea0e3ddc 100644 --- a/src/scripting/luautil.cpp +++ b/src/scripting/luautil.cpp @@ -121,23 +121,6 @@ Script *getScript(lua_State *s) valid in the map. TODO: do it. */ -Being *getBeing(lua_State *s, int p) -{ - if (!lua_islightuserdata(s, p)) - return 0; - return static_cast<Being *>(lua_touserdata(s, p)); -} - -Character *getCharacter(lua_State *s, int p) -{ - if (!lua_islightuserdata(s, p)) - return 0; - Entity *t = static_cast<Entity *>(lua_touserdata(s, p)); - if (t->getType() != OBJECT_CHARACTER) - return 0; - return static_cast<Character *>(t); -} - ItemClass *getItemClass(lua_State *s, int p) { ItemClass *itemClass = 0; @@ -158,16 +141,6 @@ ItemClass *getItemClass(lua_State *s, int p) return itemClass; } -Monster *getMonster(lua_State *s, int p) -{ - if (!lua_islightuserdata(s, p)) - return 0; - Entity *t = static_cast<Entity *>(lua_touserdata(s, p)); - if (t->getType() != OBJECT_MONSTER) - return 0; - return static_cast<Monster *>(t); -} - MonsterClass *getMonsterClass(lua_State *s, int p) { MonsterClass *monsterClass = 0; @@ -188,29 +161,28 @@ MonsterClass *getMonsterClass(lua_State *s, int p) return monsterClass; } -NPC *getNPC(lua_State *s, int p) + +Entity *checkActor(lua_State *s, int p) { - if (!lua_islightuserdata(s, p)) - return 0; - Entity *t = static_cast<Entity *>(lua_touserdata(s, p)); - if (t->getType() != OBJECT_NPC) - return 0; - return static_cast<NPC *>(t); + Entity *entity = LuaEntity::check(s, p); + luaL_argcheck(s, entity->hasComponent<ActorComponent>(), p, + "entity has no actor component"); + return entity; } - -Being *checkBeing(lua_State *s, int p) +Entity *checkBeing(lua_State *s, int p) { - Being *being = getBeing(s, p); - luaL_argcheck(s, being, p, "being expected"); - return being; + Entity *entity = LuaEntity::check(s, p); + luaL_argcheck(s, entity->hasComponent<BeingComponent>(), p, + "entity has no being component"); + return entity; } -Character *checkCharacter(lua_State *s, int p) +Entity *checkCharacter(lua_State *s, int p) { - Character *character = getCharacter(s, p); - luaL_argcheck(s, character, p, "character expected"); - return character; + Entity *entity = LuaEntity::check(s, p); + luaL_argcheck(s, entity->getType() == OBJECT_CHARACTER, p, "character expected"); + return entity; } ItemClass *checkItemClass(lua_State *s, int p) @@ -220,11 +192,11 @@ ItemClass *checkItemClass(lua_State *s, int p) return itemClass; } -Monster *checkMonster(lua_State *s, int p) +Entity *checkMonster(lua_State *s, int p) { - Monster *monster = getMonster(s, p); - luaL_argcheck(s, monster, p, "monster expected"); - return monster; + Entity *entity = LuaEntity::check(s, p); + luaL_argcheck(s, entity->getType() == OBJECT_MONSTER, p, "monster expected"); + return entity; } MonsterClass *checkMonsterClass(lua_State *s, int p) @@ -234,11 +206,11 @@ MonsterClass *checkMonsterClass(lua_State *s, int p) return monsterClass; } -NPC *checkNPC(lua_State *s, int p) +Entity *checkNpc(lua_State *s, int p) { - NPC *npc = getNPC(s, p); - luaL_argcheck(s, npc, p, "npc expected"); - return npc; + Entity *entity = LuaEntity::check(s, p); + luaL_argcheck(s, entity->getType() == OBJECT_NPC, p, "npc expected"); + return entity; } int checkSkill(lua_State *s, int p) diff --git a/src/scripting/luautil.h b/src/scripting/luautil.h index 8e380d4e..6d078e3e 100644 --- a/src/scripting/luautil.h +++ b/src/scripting/luautil.h @@ -37,15 +37,12 @@ extern "C" { #include "game-server/attack.h" #include "game-server/specialmanager.h" -class Being; -class Character; +class CharacterComponent; class Entity; class ItemClass; class MapComposite; class MapObject; -class Monster; class MonsterClass; -class NPC; class StatusEffect; void raiseWarning(lua_State *s, const char *format, ...); @@ -159,6 +156,7 @@ template <typename T> const char * LuaUserData<T>::mTypeName; typedef LuaUserData<AttackInfo> LuaAttackInfo; typedef LuaUserData<Damage> LuaDamage; +typedef LuaUserData<Entity> LuaEntity; typedef LuaUserData<ItemClass> LuaItemClass; typedef LuaUserData<MapObject> LuaMapObject; typedef LuaUserData<MonsterClass> LuaMonsterClass; @@ -167,19 +165,16 @@ typedef LuaUserData<SpecialManager::SpecialInfo> LuaSpecialInfo; Script * getScript(lua_State *s); -Being * getBeing(lua_State *s, int p); -Character * getCharacter(lua_State *s, int p); ItemClass * getItemClass(lua_State *s, int p); -Monster * getMonster(lua_State *s, int p); MonsterClass * getMonsterClass(lua_State *s, int p); -NPC * getNPC(lua_State *s, int p); -Being * checkBeing(lua_State *s, int p); -Character * checkCharacter(lua_State *s, int p); +Entity * checkActor(lua_State *s, int p); +Entity * checkBeing(lua_State *s, int p); +Entity * checkCharacter(lua_State *s, int p); ItemClass * checkItemClass(lua_State *s, int p); -Monster * checkMonster(lua_State *s, int p); +Entity * checkMonster(lua_State *s, int p); MonsterClass * checkMonsterClass(lua_State *s, int p); -NPC * checkNPC(lua_State *s, int p); +Entity * checkNpc(lua_State *s, int p); int checkSkill(lua_State *s, int p); int checkSpecial(lua_State *s, int p); @@ -202,7 +197,7 @@ inline void push(lua_State *s, const std::string &val) inline void push(lua_State *s, Entity *val) { - lua_pushlightuserdata(s, val); + LuaEntity::push(s, val); } inline void push(lua_State *s, double val) diff --git a/src/scripting/script.h b/src/scripting/script.h index 574d1b9b..7cf457cc 100644 --- a/src/scripting/script.h +++ b/src/scripting/script.h @@ -31,7 +31,6 @@ #include <sigc++/trackable.h> -class Being; class MapComposite; class Entity; @@ -44,9 +43,13 @@ class Script : public sigc::trackable struct Context { MapComposite *map; + Entity *npc; + Entity *character; Context() : map(0) + , npc(0) + , character(0) {} }; @@ -238,7 +241,7 @@ class Script : public sigc::trackable const Context *getContext() const { return mContext; } - virtual void processDeathEvent(Being *entity) = 0; + virtual void processDeathEvent(Entity *entity) = 0; virtual void processRemoveEvent(Entity *entity) = 0; diff --git a/src/scripting/scriptmanager.cpp b/src/scripting/scriptmanager.cpp index 39a1a6a5..ee231611 100644 --- a/src/scripting/scriptmanager.cpp +++ b/src/scripting/scriptmanager.cpp @@ -49,7 +49,7 @@ Script *ScriptManager::currentState() return _currentState; } -bool ScriptManager::performCraft(Being *crafter, +bool ScriptManager::performCraft(Entity *crafter, const std::list<InventoryItem> &recipe) { if (!_craftCallback.isValid()) diff --git a/src/scripting/scriptmanager.h b/src/scripting/scriptmanager.h index 03b6e64b..14b01b62 100644 --- a/src/scripting/scriptmanager.h +++ b/src/scripting/scriptmanager.h @@ -55,7 +55,7 @@ bool loadMainScript(const std::string &file); */ Script *currentState(); -bool performCraft(Being *crafter, const std::list<InventoryItem> &recipe); +bool performCraft(Entity *crafter, const std::list<InventoryItem> &recipe); void setCraftCallback(Script *script); void setSpecialCallback(Script *script); diff --git a/src/serialize/characterdata.h b/src/serialize/characterdata.h index abcc6114..c25fb096 100644 --- a/src/serialize/characterdata.h +++ b/src/serialize/characterdata.h @@ -42,16 +42,14 @@ void serializeCharacterData(const T &data, MessageOut &msg) msg.writeInt16(data.getCharacterPoints()); msg.writeInt16(data.getCorrectionPoints()); - msg.writeInt16(data.mAttributes.size()); - AttributeMap::const_iterator attr_it, attr_it_end; - for (attr_it = data.mAttributes.begin(), - attr_it_end = data.mAttributes.end(); - attr_it != attr_it_end; - ++attr_it) + + const AttributeMap &attributes = data.getAttributes(); + msg.writeInt16(attributes.size()); + for (auto attributeIt : attributes) { - msg.writeInt16(attr_it->first); - msg.writeDouble(data.getAttrBase(attr_it)); - msg.writeDouble(data.getAttrMod(attr_it)); + msg.writeInt16(attributeIt.first); + msg.writeDouble(attributeIt.second.getBase()); + msg.writeDouble(attributeIt.second.getModifiedAttribute()); } // character skills @@ -66,11 +64,11 @@ void serializeCharacterData(const T &data, MessageOut &msg) // status effects currently affecting the character msg.writeInt16(data.getStatusEffectSize()); - std::map<int, int>::const_iterator status_it; + std::map<int, Status>::const_iterator status_it; for (status_it = data.getStatusEffectBegin(); status_it != data.getStatusEffectEnd(); status_it++) { msg.writeInt16(status_it->first); - msg.writeInt16(status_it->second); + msg.writeInt16(status_it->second.time); } // location diff --git a/src/sql/mysql/updates/update_19_to_20.sql b/src/sql/mysql/updates/update_19_to_20.sql index affdc99c..dfc38878 100644 --- a/src/sql/mysql/updates/update_19_to_20.sql +++ b/src/sql/mysql/updates/update_19_to_20.sql @@ -1,4 +1,4 @@ -BEGIN; +START TRANSACTION; -- Set existing world variables to the world map (0) UPDATE mana_world_states SET map_id = 0 WHERE map_id IS NULL; @@ -22,4 +22,4 @@ UPDATE mana_world_states moddate = UNIX_TIMESTAMP() WHERE state_name = 'database_version'; -END; +COMMIT; diff --git a/src/sql/mysql/updates/update_20_to_21.sql b/src/sql/mysql/updates/update_20_to_21.sql index d66f2208..0bf9b347 100644 --- a/src/sql/mysql/updates/update_20_to_21.sql +++ b/src/sql/mysql/updates/update_20_to_21.sql @@ -1,4 +1,4 @@ -BEGIN; +START TRANSACTION; ALTER TABLE mana_char_specials ADD COLUMN special_current_mana int(10) unsigned NOT NULL; @@ -8,4 +8,4 @@ UPDATE mana_world_states moddate = UNIX_TIMESTAMP() WHERE state_name = 'database_version'; -END; +COMMIT; diff --git a/src/utils/mathutils.cpp b/src/utils/mathutils.cpp index 3208fb50..6f7bc223 100644 --- a/src/utils/mathutils.cpp +++ b/src/utils/mathutils.cpp @@ -25,6 +25,10 @@ #include <string.h> #include <float.h> +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 +#endif + static const int MATH_UTILS_MAX_ANGLE = 360; static float sinList[MATH_UTILS_MAX_ANGLE]; |