From 434d3f97489094f3f7897bce0942cd3c46483c9f Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Sun, 8 Sep 2013 11:42:33 +0200 Subject: Renamed character.cpp to charactercomponent.cpp Now we do not have two character.cpp files in the repository. --- src/game-server/accountconnection.cpp | 2 +- src/game-server/being.cpp | 2 +- src/game-server/buysell.cpp | 2 +- src/game-server/character.cpp | 583 ------------------------------- src/game-server/character.h | 382 -------------------- src/game-server/charactercomponent.cpp | 583 +++++++++++++++++++++++++++++++ src/game-server/charactercomponent.h | 382 ++++++++++++++++++++ src/game-server/commandhandler.cpp | 2 +- src/game-server/inventory.h | 2 +- src/game-server/mapcomposite.cpp | 2 +- src/game-server/monster.cpp | 2 +- src/game-server/npc.cpp | 2 +- src/game-server/postman.h | 2 +- src/game-server/quest.cpp | 2 +- src/game-server/trade.cpp | 2 +- src/game-server/triggerareacomponent.cpp | 2 +- 16 files changed, 977 insertions(+), 977 deletions(-) delete mode 100644 src/game-server/character.cpp delete mode 100644 src/game-server/character.h create mode 100644 src/game-server/charactercomponent.cpp create mode 100644 src/game-server/charactercomponent.h (limited to 'src/game-server') diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp index 604fe2ea..6b7b728a 100644 --- a/src/game-server/accountconnection.cpp +++ b/src/game-server/accountconnection.cpp @@ -21,7 +21,7 @@ #include "game-server/accountconnection.h" #include "common/configuration.h" -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "game-server/gamehandler.h" #include "game-server/map.h" #include "game-server/mapcomposite.h" diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp index 8ef74c96..8b4c2967 100644 --- a/src/game-server/being.cpp +++ b/src/game-server/being.cpp @@ -25,7 +25,7 @@ #include "common/configuration.h" #include "common/defines.h" #include "game-server/attributemanager.h" -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "game-server/collisiondetection.h" #include "game-server/mapcomposite.h" #include "game-server/effect.h" diff --git a/src/game-server/buysell.cpp b/src/game-server/buysell.cpp index ecc8793c..97f97c85 100644 --- a/src/game-server/buysell.cpp +++ b/src/game-server/buysell.cpp @@ -20,7 +20,7 @@ #include "game-server/buysell.h" -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "game-server/gamehandler.h" #include "game-server/inventory.h" #include "game-server/itemmanager.h" diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp deleted file mode 100644 index ea9228a7..00000000 --- a/src/game-server/character.cpp +++ /dev/null @@ -1,583 +0,0 @@ -/* - * The Mana Server - * Copyright (C) 2004-2010 The Mana World Development Team - * - * This file is part of The Mana Server. - * - * The Mana Server is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * The Mana Server is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Mana Server. If not, see . - */ - -#include "game-server/character.h" - -#include "common/configuration.h" -#include "game-server/accountconnection.h" -#include "game-server/attributemanager.h" -#include "game-server/buysell.h" -#include "game-server/inventory.h" -#include "game-server/item.h" -#include "game-server/itemmanager.h" -#include "game-server/gamehandler.h" -#include "game-server/map.h" -#include "game-server/mapcomposite.h" -#include "game-server/mapmanager.h" -#include "game-server/state.h" -#include "game-server/trade.h" -#include "scripting/scriptmanager.h" -#include "net/messagein.h" -#include "net/messageout.h" - -#include "utils/logger.h" - -#include -#include -#include -#include - -Script::Ref CharacterComponent::mDeathCallback; -Script::Ref CharacterComponent::mDeathAcceptedCallback; -Script::Ref CharacterComponent::mLoginCallback; - -static bool executeCallback(Script::Ref function, Entity &entity) -{ - if (!function.isValid()) - return false; - - Script *script = ScriptManager::currentState(); - script->prepare(function); - script->push(&entity); - script->execute(entity.getMap()); - return true; -} - - -CharacterComponent::CharacterComponent(Entity &entity, MessageIn &msg): - mClient(nullptr), - mConnected(true), - mTransactionHandler(nullptr), - mDatabaseID(-1), - mHairStyle(0), - mHairColor(0), - mSendAttributePointsStatus(false), - mAttributePoints(0), - mCorrectionPoints(0), - mSendAbilityCooldown(false), - mParty(0), - mTransaction(TRANS_NONE), - mTalkNpcId(0), - mNpcThread(0), - mBaseEntity(&entity) -{ - auto *beingComponent = entity.getComponent(); - - auto &attributeScope = attributeManager->getAttributeScope(CharacterScope); - LOG_DEBUG("Character creation: initialisation of " - << attributeScope.size() << " attributes."); - for (auto &attribute : attributeScope) - beingComponent->createAttribute(attribute); - - auto *actorComponent = entity.getComponent(); - actorComponent->setWalkMask(Map::BLOCKMASK_WALL); - actorComponent->setBlockType(BLOCKTYPE_CHARACTER); - actorComponent->setSize(16); - - - auto *abilityComponent = new AbilityComponent(); - entity.addComponent(abilityComponent); - abilityComponent->signal_ability_changed.connect( - sigc::mem_fun(this, &CharacterComponent::abilityStatusChanged)); - abilityComponent->signal_global_cooldown_activated.connect( - sigc::mem_fun(this, - &CharacterComponent::abilityCooldownActivated)); - - // Get character data. - mDatabaseID = msg.readInt32(); - beingComponent->setName(msg.readString()); - - deserialize(entity, msg); - - Inventory(&entity, mPossessions).initialize(); - modifiedAllAttributes(entity);; - - beingComponent->signal_attribute_changed.connect(sigc::mem_fun( - this, &CharacterComponent::attributeChanged)); - - for (auto &abilityIt : abilityComponent->getAbilities()) - mModifiedAbilities.insert(abilityIt.first); -} - -CharacterComponent::~CharacterComponent() -{ - delete mNpcThread; -} - -void CharacterComponent::deserialize(Entity &entity, MessageIn &msg) -{ - auto *beingComponent = entity.getComponent(); - - // general character properties - setAccountLevel(msg.readInt8()); - beingComponent->setGender(ManaServ::getGender(msg.readInt8())); - setHairStyle(msg.readInt8()); - setHairColor(msg.readInt8()); - setAttributePoints(msg.readInt16()); - setCorrectionPoints(msg.readInt16()); - - // character attributes - unsigned attrSize = msg.readInt16(); - for (unsigned i = 0; i < attrSize; ++i) - { - unsigned id = msg.readInt16(); - double base = msg.readDouble(); - auto *attributeInfo = attributeManager->getAttributeInfo(id); - if (attributeInfo) - beingComponent->setAttribute(entity, attributeInfo, base); - } - - // status effects currently affecting the character - int statusSize = msg.readInt16(); - - for (int i = 0; i < statusSize; i++) - { - int status = msg.readInt16(); - int time = msg.readInt16(); - beingComponent->applyStatusEffect(status, time); - } - - // location - auto *map = MapManager::getMap(msg.readInt16()); - entity.setMap(map); - - Point temporaryPoint; - temporaryPoint.x = msg.readInt16(); - temporaryPoint.y = msg.readInt16(); - entity.getComponent()->setPosition(entity, temporaryPoint); - - // kill count - int killSize = msg.readInt16(); - for (int i = 0; i < killSize; i++) - { - int monsterId = msg.readInt16(); - int kills = msg.readInt32(); - setKillCount(monsterId, kills); - } - - // character abilities - int abilitiesSize = msg.readInt16(); - for (int i = 0; i < abilitiesSize; i++) - { - const int id = msg.readInt32(); - entity.getComponent()->giveAbility(id); - } - - - Possessions &poss = getPossessions(); - - // Loads inventory - must be last because size isn't transmitted - InventoryData inventoryData; - EquipData equipmentData; - while (msg.getUnreadLength()) - { - InventoryItem i; - i.slot = msg.readInt16(); - i.itemId = msg.readInt16(); - i.amount = msg.readInt16(); - bool isEquipped = msg.readInt8() != 0; - inventoryData.insert(std::make_pair(i.slot, i)); - if (isEquipped) - equipmentData.insert(i.slot); - } - poss.setInventory(inventoryData); - poss.setEquipment(equipmentData); -} - -void CharacterComponent::serialize(Entity &entity, MessageOut &msg) -{ - auto *beingComponent = entity.getComponent(); - - // general character properties - msg.writeInt8(getAccountLevel()); - msg.writeInt8(beingComponent->getGender()); - msg.writeInt8(getHairStyle()); - msg.writeInt8(getHairColor()); - msg.writeInt16(getAttributePoints()); - msg.writeInt16(getCorrectionPoints()); - - - const AttributeMap &attributes = beingComponent->getAttributes(); - std::map attributesToSend; - for (auto &attributeIt : attributes) - { - if (attributeIt.first->persistent) - attributesToSend.insert(std::make_pair(attributeIt.first, - &attributeIt.second)); - } - msg.writeInt16(attributesToSend.size()); - for (auto &attributeIt : attributesToSend) - { - msg.writeInt16(attributeIt.first->id); - msg.writeDouble(attributeIt.second->getBase()); - msg.writeDouble(attributeIt.second->getModifiedAttribute()); - } - - // status effects currently affecting the character - auto &statusEffects = beingComponent->getStatusEffects(); - msg.writeInt16(statusEffects.size()); - for (auto &statusIt : statusEffects) - { - msg.writeInt16(statusIt.first); - msg.writeInt16(statusIt.second.time); - } - - // location - msg.writeInt16(entity.getMap()->getID()); - const Point &pos = entity.getComponent()->getPosition(); - msg.writeInt16(pos.x); - msg.writeInt16(pos.y); - - // kill count - msg.writeInt16(getKillCountSize()); - for (auto &killCountIt : mKillCount) - { - msg.writeInt16(killCountIt.first); - msg.writeInt32(killCountIt.second); - } - - // character abilities - auto &abilities = entity.getComponent()->getAbilities(); - msg.writeInt16(abilities.size()); - for (auto &abilityIt : abilities) { - msg.writeInt32(abilityIt.first); - } - - // inventory - must be last because size isn't transmitted - const Possessions &poss = getPossessions(); - const EquipData &equipData = poss.getEquipment(); - - const InventoryData &inventoryData = poss.getInventory(); - for (InventoryData::const_iterator itemIt = inventoryData.begin(), - itemIt_end = inventoryData.end(); itemIt != itemIt_end; ++itemIt) - { - msg.writeInt16(itemIt->first); // slot id - msg.writeInt16(itemIt->second.itemId); // item id - msg.writeInt16(itemIt->second.amount); // amount - if (equipData.find(itemIt->first) != equipData.end()) - msg.writeInt8(1); // equipped - else - msg.writeInt8(0); // not equipped - } -} - -void CharacterComponent::update(Entity &entity) -{ - // Dead character: don't regenerate anything else - if (entity.getComponent()->getAction() == DEAD) - return; - - if (!mModifiedAbilities.empty()) - sendAbilityUpdate(entity); - - if (mSendAbilityCooldown) - sendAbilityCooldownUpdate(entity); - - if (mSendAttributePointsStatus) - sendAttributePointsStatus(entity); -} - -void CharacterComponent::characterDied(Entity *being) -{ - executeCallback(mDeathCallback, *being); -} - -void CharacterComponent::respawn(Entity &entity) -{ - auto *beingComponent = entity.getComponent(); - - if (beingComponent->getAction() != DEAD) - { - LOG_WARN("Character \"" << beingComponent->getName() - << "\" tried to respawn without being dead"); - return; - } - - // Make it alive again - beingComponent->setAction(entity, STAND); - - // Execute respawn callback when set - if (executeCallback(mDeathAcceptedCallback, entity)) - return; - - // No script respawn callback set - fall back to hardcoded logic - const double maxHp = beingComponent->getModifiedAttribute( - attributeManager->getAttributeInfo(ATTR_MAX_HP)); - beingComponent->setAttribute(entity, - attributeManager->getAttributeInfo(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(&entity, MapManager::getMap(spawnMap), - Point(spawnX, spawnY)); -} - -void CharacterComponent::abilityStatusChanged(int id) -{ - mModifiedAbilities.insert(id); -} - -void CharacterComponent::abilityCooldownActivated() -{ - mSendAbilityCooldown = true; -} - -void CharacterComponent::sendAbilityUpdate(Entity &entity) -{ - auto &abilities = entity.getComponent()->getAbilities(); - - MessageOut msg(GPMSG_ABILITY_STATUS); - for (unsigned id : mModifiedAbilities) - { - auto it = abilities.find(id); - if (it == abilities.end()) - continue; // got deleted - - msg.writeInt8(id); - msg.writeInt32(it->second.rechargeTimeout.remaining()); - } - - mModifiedAbilities.clear(); - gameHandler->sendTo(mClient, msg); -} - -void CharacterComponent::sendAbilityCooldownUpdate(Entity &entity) -{ - MessageOut msg(GPMSG_ABILITY_COOLDOWN); - auto *abilityComponent = entity.getComponent(); - msg.writeInt16(abilityComponent->globalCooldown()); - gameHandler->sendTo(mClient, msg); - mSendAbilityCooldown = false; -} - -void CharacterComponent::sendAttributePointsStatus(Entity &entity) -{ - MessageOut msg(GPMSG_ATTRIBUTE_POINTS_STATUS); - msg.writeInt16(mAttributePoints); - msg.writeInt16(mCorrectionPoints); - gameHandler->sendTo(mClient, msg); - mSendAttributePointsStatus = false; -} - -void CharacterComponent::cancelTransaction() -{ - TransactionType t = mTransaction; - mTransaction = TRANS_NONE; - switch (t) - { - case TRANS_TRADE: - static_cast< Trade * >(mTransactionHandler)->cancel(); - break; - case TRANS_BUYSELL: - static_cast< BuySell * >(mTransactionHandler)->cancel(); - break; - case TRANS_NONE: - return; - } -} - -Trade *CharacterComponent::getTrading() const -{ - return mTransaction == TRANS_TRADE - ? static_cast< Trade * >(mTransactionHandler) : nullptr; -} - -BuySell *CharacterComponent::getBuySell() const -{ - return mTransaction == TRANS_BUYSELL - ? static_cast< BuySell * >(mTransactionHandler) : nullptr; -} - -void CharacterComponent::setTrading(Trade *t) -{ - if (t) - { - cancelTransaction(); - mTransactionHandler = t; - mTransaction = TRANS_TRADE; - } - else - { - assert(mTransaction == TRANS_NONE || mTransaction == TRANS_TRADE); - mTransaction = TRANS_NONE; - } -} - -void CharacterComponent::setBuySell(BuySell *t) -{ - if (t) - { - cancelTransaction(); - mTransactionHandler = t; - mTransaction = TRANS_BUYSELL; - } - else - { - assert(mTransaction == TRANS_NONE || mTransaction == TRANS_BUYSELL); - mTransaction = TRANS_NONE; - } -} - -void CharacterComponent::sendStatus(Entity &entity) -{ - auto *beingComponent = entity.getComponent(); - MessageOut attribMsg(GPMSG_PLAYER_ATTRIBUTE_CHANGE); - for (AttributeInfo *attribute : mModifiedAttributes) - { - attribMsg.writeInt16(attribute->id); - attribMsg.writeInt32(beingComponent->getAttributeBase(attribute) * 256); - attribMsg.writeInt32(beingComponent->getModifiedAttribute(attribute) * 256); - } - if (attribMsg.getLength() > 2) gameHandler->sendTo(mClient, attribMsg); - mModifiedAttributes.clear(); -} - -void CharacterComponent::modifiedAllAttributes(Entity &entity) -{ - auto *beingComponent = entity.getComponent(); - - LOG_DEBUG("Marking all attributes as changed, requiring recalculation."); - for (auto attribute : beingComponent->getAttributes()) - { - beingComponent->recalculateBaseAttribute(entity, attribute.first); - mModifiedAttributes.insert(attribute.first); - } -} - -void CharacterComponent::attributeChanged(Entity *entity, - AttributeInfo *attribute) -{ - auto *beingComponent = entity->getComponent(); - - // Inform the client of this attribute modification. - accountHandler->updateAttributes(getDatabaseID(), attribute->id, - beingComponent->getAttributeBase(attribute), - beingComponent->getModifiedAttribute(attribute)); - mModifiedAttributes.insert(attribute); -} - -void CharacterComponent::incrementKillCount(int monsterType) -{ - std::map::iterator i = mKillCount.find(monsterType); - if (i == mKillCount.end()) - { - // Character has never murdered this species before - mKillCount[monsterType] = 1; - } - else - { - // Character is a repeated offender - mKillCount[monsterType] ++; - } -} - -int CharacterComponent::getKillCount(int monsterType) const -{ - std::map::const_iterator i = mKillCount.find(monsterType); - if (i != mKillCount.end()) - return i->second; - return 0; -} - -AttribmodResponseCode CharacterComponent::useCharacterPoint(Entity &entity, - AttributeInfo *attribute) -{ - auto *beingComponent = entity.getComponent(); - - if (!attribute->modifiable) - return ATTRIBMOD_INVALID_ATTRIBUTE; - if (!mAttributePoints) - return ATTRIBMOD_NO_POINTS_LEFT; - - setAttributePoints(mAttributePoints - 1); - - const double base = beingComponent->getAttributeBase(attribute); - beingComponent->setAttribute(entity, attribute, base + 1); - beingComponent->updateDerivedAttributes(entity, attribute); - return ATTRIBMOD_OK; -} - -AttribmodResponseCode CharacterComponent::useCorrectionPoint(Entity &entity, - AttributeInfo *attribute) -{ - auto *beingComponent = entity.getComponent(); - - if (!attribute->modifiable) - return ATTRIBMOD_INVALID_ATTRIBUTE; - if (!mCorrectionPoints) - return ATTRIBMOD_NO_POINTS_LEFT; - if (beingComponent->getAttributeBase(attribute) <= 1) - return ATTRIBMOD_DENIED; - - setCorrectionPoints(mCorrectionPoints - 1); - setAttributePoints(mAttributePoints + 1); - - const double base = beingComponent->getAttributeBase(attribute); - beingComponent->setAttribute(entity, attribute, base - 1); - return ATTRIBMOD_OK; -} - -void CharacterComponent::startNpcThread(Script::Thread *thread, int npcId) -{ - if (mNpcThread) - delete mNpcThread; - - mNpcThread = thread; - mTalkNpcId = npcId; - - resumeNpcThread(); -} - -void CharacterComponent::resumeNpcThread() -{ - Script *script = ScriptManager::currentState(); - - assert(script->getCurrentThread() == mNpcThread); - - if (script->resume()) - { - MessageOut msg(GPMSG_NPC_CLOSE); - msg.writeInt16(mTalkNpcId); - gameHandler->sendTo(mClient, msg); - - mTalkNpcId = 0; - mNpcThread = 0; - } -} - -void CharacterComponent::disconnected(Entity &entity) -{ - mConnected = false; - - // Make the dead characters respawn, even in case of disconnection. - if (entity.getComponent()->getAction() == DEAD) - respawn(entity); - else - GameState::remove(&entity); - - signal_disconnected.emit(entity); -} -void CharacterComponent::triggerLoginCallback(Entity &entity) -{ - executeCallback(mLoginCallback, entity); -} diff --git a/src/game-server/character.h b/src/game-server/character.h deleted file mode 100644 index 57e9d44c..00000000 --- a/src/game-server/character.h +++ /dev/null @@ -1,382 +0,0 @@ -/* - * The Mana Server - * Copyright (C) 2004-2010 The Mana World Development Team - * - * This file is part of The Mana Server. - * - * The Mana Server is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * The Mana Server is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Mana Server. If not, see . - */ - -#ifndef CHARACTER_H -#define CHARACTER_H - -#include "common/defines.h" -#include "common/inventorydata.h" -#include "common/manaserv_protocol.h" - -#include "game-server/abilitycomponent.h" -#include "game-server/being.h" -#include "game-server/mapcomposite.h" -#include "game-server/mapmanager.h" -#include "game-server/abilitymanager.h" - -#include "scripting/script.h" - -#include "utils/logger.h" - -#include -#include -#include -#include - -class BuySell; -class GameClient; -class MessageIn; -class MessageOut; -class Point; -class Trade; - -/** - * The representation of a player's character in the game world. - */ -class CharacterComponent : public Component -{ - public: - static const ComponentType type = CT_Character; - - /** - * Utility constructor for creating a Character from a received - * characterdata message. - */ - CharacterComponent(Entity &entity, MessageIn &msg); - - ~CharacterComponent(); - - /** - * calls Being::update and handles special recharges and status effects - */ - void update(Entity &entity); - - /** - * Executes the global die script - */ - virtual void characterDied(Entity *); - - /** - * makes the character respawn - */ - void respawn(Entity &entity); - - /** - * Gets client computer. - */ - GameClient *getClient() const - { return mClient; } - - /** - * Sets client computer. - */ - void setClient(GameClient *c) - { mClient = c; } - - /** - * Gets a reference to the possessions. - */ - const Possessions &getPossessions() const - { return mPossessions; } - - /** - * Gets a reference to the possessions. - */ - Possessions &getPossessions() - { return mPossessions; } - - /** - * Gets the Trade object the character is involved in. - */ - Trade *getTrading() const; - - /** - * Sets the Trade object the character is involved in. - * Cancels other transactions. - */ - void setTrading(Trade *t); - - /** - * Gets the BuySell object the character is involved in. - */ - BuySell *getBuySell() const; - - /** - * Sets the trade object the character is involved in. - * Cancels other transactions. - */ - void setBuySell(BuySell *t); - - /** - * Cancels current transaction. - */ - void cancelTransaction(); - - /** - * Gets transaction status of the character. - */ - bool isBusy() const - { return mTransaction != TRANS_NONE; } - - /* - * Character data: - * Get and set methods - * Most of this should be accessed directly as a friend - */ - - int getDatabaseID() const { return mDatabaseID; } - void setDatabaseID(int id) { mDatabaseID = id; } - - int getHairStyle() const { return mHairStyle; } - void setHairStyle(int style) { mHairStyle = style; } - - int getHairColor() const { return mHairColor; } - void setHairColor(int color) { mHairColor = color; } - - int getAccountLevel() const { return mAccountLevel; } - void setAccountLevel(int l) { mAccountLevel = l; } - - /** Gets the party id of the character */ - int getParty() const - { return mParty; } - - /** Sets the party id of the character */ - void setParty(int party) - { mParty = party; } - - /** - * Sends a message that informs the client about attribute - * modified since last call. - */ - void sendStatus(Entity &entity); - - /** - * Marks all attributes as being modified. - */ - void modifiedAllAttributes(Entity &entity); - - /** - * 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 attributeChanged(Entity *being, AttributeInfo *); - - /** - * Calls all the "disconnected" listener. - */ - void disconnected(Entity &entity); - - /** - * Associative array containing all the quest variables known by the - * server. - */ - std::map< std::string, std::string > questCache; - - /** - * Used to serialize kill count. - */ - int getKillCountSize() const - { return mKillCount.size(); } - - const std::map::const_iterator getKillCountBegin() const - { return mKillCount.begin(); } - - const std::map::const_iterator getKillCountEnd() const - { return mKillCount.end(); } - - void setKillCount(int monsterId, int kills) - { mKillCount[monsterId] = kills; } - - /** - * Adds one kill of the monster type to the characters kill count. - */ - void incrementKillCount(int monsterType); - - /** - * Gets the number of monsters the character killed of a given type. - */ - int getKillCount(int monsterType) const; - - /** - * Tries to use a character point to increase a - * basic attribute - */ - AttribmodResponseCode useCharacterPoint(Entity &entity, - AttributeInfo *); - - /** - * Tries to use a correction point to reduce a - * basic attribute and regain a character point - */ - AttribmodResponseCode useCorrectionPoint(Entity &entity, - AttributeInfo *); - - void setAttributePoints(int points); - int getAttributePoints() const; - - void setCorrectionPoints(int points); - int getCorrectionPoints() const; - - - /** - * Starts the given NPC thread. - * - * Should be called immediately after creating the thread and pushing - * the NPC function and its parameters. - */ - void startNpcThread(Script::Thread *thread, int npcId); - - /** - * Resumes the given NPC thread of this character and sends the NPC - * close message to the player when the script is done. - * - * Should be called after preparing the current Script instance for - * resuming the thread and pushing the parameters the script expects. - */ - void resumeNpcThread(); - - /** - * Returns the NPC thread in use by this character, if any. - */ - Script::Thread *getNpcThread() const - { return mNpcThread; } - - /** Makes it impossible to chat for a while */ - void mute(int seconds) - { mMuteTimeout.set(seconds * 10); } - - bool isMuted() const - { return !mMuteTimeout.expired(); } - - bool isConnected() const - { return mConnected; } - - static void setDeathCallback(Script *script) - { script->assignCallback(mDeathCallback); } - - static void setDeathAcceptedCallback(Script *script) - { script->assignCallback(mDeathAcceptedCallback); } - - static void setLoginCallback(Script *script) - { script->assignCallback(mLoginCallback); } - - void triggerLoginCallback(Entity &entity); - - sigc::signal signal_disconnected; - - void serialize(Entity &entity, MessageOut &msg); - - private: - void deserialize(Entity &entity, MessageIn &msg); - - double getAttrBase(AttributeMap::const_iterator it) const - { return it->second.getBase(); } - double getAttrMod(AttributeMap::const_iterator it) const - { return it->second.getModifiedAttribute(); } - - CharacterComponent(const CharacterComponent &); - CharacterComponent &operator=(const CharacterComponent &); - - void abilityStatusChanged(int id); - void abilityCooldownActivated(); - - void sendAbilityUpdate(Entity &entity); - void sendAbilityCooldownUpdate(Entity &entity); - void sendAttributePointsStatus(Entity &entity); - - enum TransactionType - { TRANS_NONE, TRANS_TRADE, TRANS_BUYSELL }; - - GameClient *mClient; /**< Client computer. */ - - /** - * Tells whether the character client is connected. - * Useful when dealing with enqueued events. - */ - bool mConnected; - - /** Handler of the transaction the character is involved in. */ - void *mTransactionHandler; - - Possessions mPossessions; /**< Possesssions of the character. */ - - /** Attributes modified since last update. */ - std::set mModifiedAttributes; - - std::set mModifiedAbilities; - - int mDatabaseID; /**< Character's database ID. */ - unsigned char mHairStyle; /**< Hair Style of the character. */ - unsigned char mHairColor; /**< Hair Color of the character. */ - - bool mSendAttributePointsStatus; - int mAttributePoints; /**< Unused attribute points that can be distributed */ - int mCorrectionPoints; /**< Unused attribute correction points */ - - bool mSendAbilityCooldown; - unsigned char mAccountLevel; /**< Account level of the user. */ - int mParty; /**< Party id of the character */ - TransactionType mTransaction; /**< Trade/buy/sell action the character is involved in. */ - std::map mKillCount; /**< How many monsters the character has slain of each type */ - - int mTalkNpcId; /**< Public ID of NPC the character is talking to, if any */ - Script::Thread *mNpcThread; /**< Script thread executing NPC interaction, if any */ - - Timeout mMuteTimeout; /**< Time until the character is no longer muted */ - - 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; -}; - - -inline void CharacterComponent::setAttributePoints(int points) -{ - mSendAttributePointsStatus = true; - mAttributePoints = points; -} - -inline int CharacterComponent::getAttributePoints() const -{ - return mAttributePoints; -} - -inline void CharacterComponent::setCorrectionPoints(int points) -{ - mSendAttributePointsStatus = true; - mCorrectionPoints = points; -} - -inline int CharacterComponent::getCorrectionPoints() const -{ - return mCorrectionPoints; -} - -#endif // CHARACTER_H diff --git a/src/game-server/charactercomponent.cpp b/src/game-server/charactercomponent.cpp new file mode 100644 index 00000000..8a50d7cd --- /dev/null +++ b/src/game-server/charactercomponent.cpp @@ -0,0 +1,583 @@ +/* + * The Mana Server + * Copyright (C) 2004-2010 The Mana World Development Team + * + * This file is part of The Mana Server. + * + * The Mana Server is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana Server. If not, see . + */ + +#include "game-server/charactercomponent.h" + +#include "common/configuration.h" +#include "game-server/accountconnection.h" +#include "game-server/attributemanager.h" +#include "game-server/buysell.h" +#include "game-server/inventory.h" +#include "game-server/item.h" +#include "game-server/itemmanager.h" +#include "game-server/gamehandler.h" +#include "game-server/map.h" +#include "game-server/mapcomposite.h" +#include "game-server/mapmanager.h" +#include "game-server/state.h" +#include "game-server/trade.h" +#include "scripting/scriptmanager.h" +#include "net/messagein.h" +#include "net/messageout.h" + +#include "utils/logger.h" + +#include +#include +#include +#include + +Script::Ref CharacterComponent::mDeathCallback; +Script::Ref CharacterComponent::mDeathAcceptedCallback; +Script::Ref CharacterComponent::mLoginCallback; + +static bool executeCallback(Script::Ref function, Entity &entity) +{ + if (!function.isValid()) + return false; + + Script *script = ScriptManager::currentState(); + script->prepare(function); + script->push(&entity); + script->execute(entity.getMap()); + return true; +} + + +CharacterComponent::CharacterComponent(Entity &entity, MessageIn &msg): + mClient(nullptr), + mConnected(true), + mTransactionHandler(nullptr), + mDatabaseID(-1), + mHairStyle(0), + mHairColor(0), + mSendAttributePointsStatus(false), + mAttributePoints(0), + mCorrectionPoints(0), + mSendAbilityCooldown(false), + mParty(0), + mTransaction(TRANS_NONE), + mTalkNpcId(0), + mNpcThread(0), + mBaseEntity(&entity) +{ + auto *beingComponent = entity.getComponent(); + + auto &attributeScope = attributeManager->getAttributeScope(CharacterScope); + LOG_DEBUG("Character creation: initialisation of " + << attributeScope.size() << " attributes."); + for (auto &attribute : attributeScope) + beingComponent->createAttribute(attribute); + + auto *actorComponent = entity.getComponent(); + actorComponent->setWalkMask(Map::BLOCKMASK_WALL); + actorComponent->setBlockType(BLOCKTYPE_CHARACTER); + actorComponent->setSize(16); + + + auto *abilityComponent = new AbilityComponent(); + entity.addComponent(abilityComponent); + abilityComponent->signal_ability_changed.connect( + sigc::mem_fun(this, &CharacterComponent::abilityStatusChanged)); + abilityComponent->signal_global_cooldown_activated.connect( + sigc::mem_fun(this, + &CharacterComponent::abilityCooldownActivated)); + + // Get character data. + mDatabaseID = msg.readInt32(); + beingComponent->setName(msg.readString()); + + deserialize(entity, msg); + + Inventory(&entity, mPossessions).initialize(); + modifiedAllAttributes(entity);; + + beingComponent->signal_attribute_changed.connect(sigc::mem_fun( + this, &CharacterComponent::attributeChanged)); + + for (auto &abilityIt : abilityComponent->getAbilities()) + mModifiedAbilities.insert(abilityIt.first); +} + +CharacterComponent::~CharacterComponent() +{ + delete mNpcThread; +} + +void CharacterComponent::deserialize(Entity &entity, MessageIn &msg) +{ + auto *beingComponent = entity.getComponent(); + + // general character properties + setAccountLevel(msg.readInt8()); + beingComponent->setGender(ManaServ::getGender(msg.readInt8())); + setHairStyle(msg.readInt8()); + setHairColor(msg.readInt8()); + setAttributePoints(msg.readInt16()); + setCorrectionPoints(msg.readInt16()); + + // character attributes + unsigned attrSize = msg.readInt16(); + for (unsigned i = 0; i < attrSize; ++i) + { + unsigned id = msg.readInt16(); + double base = msg.readDouble(); + auto *attributeInfo = attributeManager->getAttributeInfo(id); + if (attributeInfo) + beingComponent->setAttribute(entity, attributeInfo, base); + } + + // status effects currently affecting the character + int statusSize = msg.readInt16(); + + for (int i = 0; i < statusSize; i++) + { + int status = msg.readInt16(); + int time = msg.readInt16(); + beingComponent->applyStatusEffect(status, time); + } + + // location + auto *map = MapManager::getMap(msg.readInt16()); + entity.setMap(map); + + Point temporaryPoint; + temporaryPoint.x = msg.readInt16(); + temporaryPoint.y = msg.readInt16(); + entity.getComponent()->setPosition(entity, temporaryPoint); + + // kill count + int killSize = msg.readInt16(); + for (int i = 0; i < killSize; i++) + { + int monsterId = msg.readInt16(); + int kills = msg.readInt32(); + setKillCount(monsterId, kills); + } + + // character abilities + int abilitiesSize = msg.readInt16(); + for (int i = 0; i < abilitiesSize; i++) + { + const int id = msg.readInt32(); + entity.getComponent()->giveAbility(id); + } + + + Possessions &poss = getPossessions(); + + // Loads inventory - must be last because size isn't transmitted + InventoryData inventoryData; + EquipData equipmentData; + while (msg.getUnreadLength()) + { + InventoryItem i; + i.slot = msg.readInt16(); + i.itemId = msg.readInt16(); + i.amount = msg.readInt16(); + bool isEquipped = msg.readInt8() != 0; + inventoryData.insert(std::make_pair(i.slot, i)); + if (isEquipped) + equipmentData.insert(i.slot); + } + poss.setInventory(inventoryData); + poss.setEquipment(equipmentData); +} + +void CharacterComponent::serialize(Entity &entity, MessageOut &msg) +{ + auto *beingComponent = entity.getComponent(); + + // general character properties + msg.writeInt8(getAccountLevel()); + msg.writeInt8(beingComponent->getGender()); + msg.writeInt8(getHairStyle()); + msg.writeInt8(getHairColor()); + msg.writeInt16(getAttributePoints()); + msg.writeInt16(getCorrectionPoints()); + + + const AttributeMap &attributes = beingComponent->getAttributes(); + std::map attributesToSend; + for (auto &attributeIt : attributes) + { + if (attributeIt.first->persistent) + attributesToSend.insert(std::make_pair(attributeIt.first, + &attributeIt.second)); + } + msg.writeInt16(attributesToSend.size()); + for (auto &attributeIt : attributesToSend) + { + msg.writeInt16(attributeIt.first->id); + msg.writeDouble(attributeIt.second->getBase()); + msg.writeDouble(attributeIt.second->getModifiedAttribute()); + } + + // status effects currently affecting the character + auto &statusEffects = beingComponent->getStatusEffects(); + msg.writeInt16(statusEffects.size()); + for (auto &statusIt : statusEffects) + { + msg.writeInt16(statusIt.first); + msg.writeInt16(statusIt.second.time); + } + + // location + msg.writeInt16(entity.getMap()->getID()); + const Point &pos = entity.getComponent()->getPosition(); + msg.writeInt16(pos.x); + msg.writeInt16(pos.y); + + // kill count + msg.writeInt16(getKillCountSize()); + for (auto &killCountIt : mKillCount) + { + msg.writeInt16(killCountIt.first); + msg.writeInt32(killCountIt.second); + } + + // character abilities + auto &abilities = entity.getComponent()->getAbilities(); + msg.writeInt16(abilities.size()); + for (auto &abilityIt : abilities) { + msg.writeInt32(abilityIt.first); + } + + // inventory - must be last because size isn't transmitted + const Possessions &poss = getPossessions(); + const EquipData &equipData = poss.getEquipment(); + + const InventoryData &inventoryData = poss.getInventory(); + for (InventoryData::const_iterator itemIt = inventoryData.begin(), + itemIt_end = inventoryData.end(); itemIt != itemIt_end; ++itemIt) + { + msg.writeInt16(itemIt->first); // slot id + msg.writeInt16(itemIt->second.itemId); // item id + msg.writeInt16(itemIt->second.amount); // amount + if (equipData.find(itemIt->first) != equipData.end()) + msg.writeInt8(1); // equipped + else + msg.writeInt8(0); // not equipped + } +} + +void CharacterComponent::update(Entity &entity) +{ + // Dead character: don't regenerate anything else + if (entity.getComponent()->getAction() == DEAD) + return; + + if (!mModifiedAbilities.empty()) + sendAbilityUpdate(entity); + + if (mSendAbilityCooldown) + sendAbilityCooldownUpdate(entity); + + if (mSendAttributePointsStatus) + sendAttributePointsStatus(entity); +} + +void CharacterComponent::characterDied(Entity *being) +{ + executeCallback(mDeathCallback, *being); +} + +void CharacterComponent::respawn(Entity &entity) +{ + auto *beingComponent = entity.getComponent(); + + if (beingComponent->getAction() != DEAD) + { + LOG_WARN("Character \"" << beingComponent->getName() + << "\" tried to respawn without being dead"); + return; + } + + // Make it alive again + beingComponent->setAction(entity, STAND); + + // Execute respawn callback when set + if (executeCallback(mDeathAcceptedCallback, entity)) + return; + + // No script respawn callback set - fall back to hardcoded logic + const double maxHp = beingComponent->getModifiedAttribute( + attributeManager->getAttributeInfo(ATTR_MAX_HP)); + beingComponent->setAttribute(entity, + attributeManager->getAttributeInfo(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(&entity, MapManager::getMap(spawnMap), + Point(spawnX, spawnY)); +} + +void CharacterComponent::abilityStatusChanged(int id) +{ + mModifiedAbilities.insert(id); +} + +void CharacterComponent::abilityCooldownActivated() +{ + mSendAbilityCooldown = true; +} + +void CharacterComponent::sendAbilityUpdate(Entity &entity) +{ + auto &abilities = entity.getComponent()->getAbilities(); + + MessageOut msg(GPMSG_ABILITY_STATUS); + for (unsigned id : mModifiedAbilities) + { + auto it = abilities.find(id); + if (it == abilities.end()) + continue; // got deleted + + msg.writeInt8(id); + msg.writeInt32(it->second.rechargeTimeout.remaining()); + } + + mModifiedAbilities.clear(); + gameHandler->sendTo(mClient, msg); +} + +void CharacterComponent::sendAbilityCooldownUpdate(Entity &entity) +{ + MessageOut msg(GPMSG_ABILITY_COOLDOWN); + auto *abilityComponent = entity.getComponent(); + msg.writeInt16(abilityComponent->globalCooldown()); + gameHandler->sendTo(mClient, msg); + mSendAbilityCooldown = false; +} + +void CharacterComponent::sendAttributePointsStatus(Entity &entity) +{ + MessageOut msg(GPMSG_ATTRIBUTE_POINTS_STATUS); + msg.writeInt16(mAttributePoints); + msg.writeInt16(mCorrectionPoints); + gameHandler->sendTo(mClient, msg); + mSendAttributePointsStatus = false; +} + +void CharacterComponent::cancelTransaction() +{ + TransactionType t = mTransaction; + mTransaction = TRANS_NONE; + switch (t) + { + case TRANS_TRADE: + static_cast< Trade * >(mTransactionHandler)->cancel(); + break; + case TRANS_BUYSELL: + static_cast< BuySell * >(mTransactionHandler)->cancel(); + break; + case TRANS_NONE: + return; + } +} + +Trade *CharacterComponent::getTrading() const +{ + return mTransaction == TRANS_TRADE + ? static_cast< Trade * >(mTransactionHandler) : nullptr; +} + +BuySell *CharacterComponent::getBuySell() const +{ + return mTransaction == TRANS_BUYSELL + ? static_cast< BuySell * >(mTransactionHandler) : nullptr; +} + +void CharacterComponent::setTrading(Trade *t) +{ + if (t) + { + cancelTransaction(); + mTransactionHandler = t; + mTransaction = TRANS_TRADE; + } + else + { + assert(mTransaction == TRANS_NONE || mTransaction == TRANS_TRADE); + mTransaction = TRANS_NONE; + } +} + +void CharacterComponent::setBuySell(BuySell *t) +{ + if (t) + { + cancelTransaction(); + mTransactionHandler = t; + mTransaction = TRANS_BUYSELL; + } + else + { + assert(mTransaction == TRANS_NONE || mTransaction == TRANS_BUYSELL); + mTransaction = TRANS_NONE; + } +} + +void CharacterComponent::sendStatus(Entity &entity) +{ + auto *beingComponent = entity.getComponent(); + MessageOut attribMsg(GPMSG_PLAYER_ATTRIBUTE_CHANGE); + for (AttributeInfo *attribute : mModifiedAttributes) + { + attribMsg.writeInt16(attribute->id); + attribMsg.writeInt32(beingComponent->getAttributeBase(attribute) * 256); + attribMsg.writeInt32(beingComponent->getModifiedAttribute(attribute) * 256); + } + if (attribMsg.getLength() > 2) gameHandler->sendTo(mClient, attribMsg); + mModifiedAttributes.clear(); +} + +void CharacterComponent::modifiedAllAttributes(Entity &entity) +{ + auto *beingComponent = entity.getComponent(); + + LOG_DEBUG("Marking all attributes as changed, requiring recalculation."); + for (auto attribute : beingComponent->getAttributes()) + { + beingComponent->recalculateBaseAttribute(entity, attribute.first); + mModifiedAttributes.insert(attribute.first); + } +} + +void CharacterComponent::attributeChanged(Entity *entity, + AttributeInfo *attribute) +{ + auto *beingComponent = entity->getComponent(); + + // Inform the client of this attribute modification. + accountHandler->updateAttributes(getDatabaseID(), attribute->id, + beingComponent->getAttributeBase(attribute), + beingComponent->getModifiedAttribute(attribute)); + mModifiedAttributes.insert(attribute); +} + +void CharacterComponent::incrementKillCount(int monsterType) +{ + std::map::iterator i = mKillCount.find(monsterType); + if (i == mKillCount.end()) + { + // Character has never murdered this species before + mKillCount[monsterType] = 1; + } + else + { + // Character is a repeated offender + mKillCount[monsterType] ++; + } +} + +int CharacterComponent::getKillCount(int monsterType) const +{ + std::map::const_iterator i = mKillCount.find(monsterType); + if (i != mKillCount.end()) + return i->second; + return 0; +} + +AttribmodResponseCode CharacterComponent::useCharacterPoint(Entity &entity, + AttributeInfo *attribute) +{ + auto *beingComponent = entity.getComponent(); + + if (!attribute->modifiable) + return ATTRIBMOD_INVALID_ATTRIBUTE; + if (!mAttributePoints) + return ATTRIBMOD_NO_POINTS_LEFT; + + setAttributePoints(mAttributePoints - 1); + + const double base = beingComponent->getAttributeBase(attribute); + beingComponent->setAttribute(entity, attribute, base + 1); + beingComponent->updateDerivedAttributes(entity, attribute); + return ATTRIBMOD_OK; +} + +AttribmodResponseCode CharacterComponent::useCorrectionPoint(Entity &entity, + AttributeInfo *attribute) +{ + auto *beingComponent = entity.getComponent(); + + if (!attribute->modifiable) + return ATTRIBMOD_INVALID_ATTRIBUTE; + if (!mCorrectionPoints) + return ATTRIBMOD_NO_POINTS_LEFT; + if (beingComponent->getAttributeBase(attribute) <= 1) + return ATTRIBMOD_DENIED; + + setCorrectionPoints(mCorrectionPoints - 1); + setAttributePoints(mAttributePoints + 1); + + const double base = beingComponent->getAttributeBase(attribute); + beingComponent->setAttribute(entity, attribute, base - 1); + return ATTRIBMOD_OK; +} + +void CharacterComponent::startNpcThread(Script::Thread *thread, int npcId) +{ + if (mNpcThread) + delete mNpcThread; + + mNpcThread = thread; + mTalkNpcId = npcId; + + resumeNpcThread(); +} + +void CharacterComponent::resumeNpcThread() +{ + Script *script = ScriptManager::currentState(); + + assert(script->getCurrentThread() == mNpcThread); + + if (script->resume()) + { + MessageOut msg(GPMSG_NPC_CLOSE); + msg.writeInt16(mTalkNpcId); + gameHandler->sendTo(mClient, msg); + + mTalkNpcId = 0; + mNpcThread = 0; + } +} + +void CharacterComponent::disconnected(Entity &entity) +{ + mConnected = false; + + // Make the dead characters respawn, even in case of disconnection. + if (entity.getComponent()->getAction() == DEAD) + respawn(entity); + else + GameState::remove(&entity); + + signal_disconnected.emit(entity); +} +void CharacterComponent::triggerLoginCallback(Entity &entity) +{ + executeCallback(mLoginCallback, entity); +} diff --git a/src/game-server/charactercomponent.h b/src/game-server/charactercomponent.h new file mode 100644 index 00000000..57e9d44c --- /dev/null +++ b/src/game-server/charactercomponent.h @@ -0,0 +1,382 @@ +/* + * The Mana Server + * Copyright (C) 2004-2010 The Mana World Development Team + * + * This file is part of The Mana Server. + * + * The Mana Server is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana Server. If not, see . + */ + +#ifndef CHARACTER_H +#define CHARACTER_H + +#include "common/defines.h" +#include "common/inventorydata.h" +#include "common/manaserv_protocol.h" + +#include "game-server/abilitycomponent.h" +#include "game-server/being.h" +#include "game-server/mapcomposite.h" +#include "game-server/mapmanager.h" +#include "game-server/abilitymanager.h" + +#include "scripting/script.h" + +#include "utils/logger.h" + +#include +#include +#include +#include + +class BuySell; +class GameClient; +class MessageIn; +class MessageOut; +class Point; +class Trade; + +/** + * The representation of a player's character in the game world. + */ +class CharacterComponent : public Component +{ + public: + static const ComponentType type = CT_Character; + + /** + * Utility constructor for creating a Character from a received + * characterdata message. + */ + CharacterComponent(Entity &entity, MessageIn &msg); + + ~CharacterComponent(); + + /** + * calls Being::update and handles special recharges and status effects + */ + void update(Entity &entity); + + /** + * Executes the global die script + */ + virtual void characterDied(Entity *); + + /** + * makes the character respawn + */ + void respawn(Entity &entity); + + /** + * Gets client computer. + */ + GameClient *getClient() const + { return mClient; } + + /** + * Sets client computer. + */ + void setClient(GameClient *c) + { mClient = c; } + + /** + * Gets a reference to the possessions. + */ + const Possessions &getPossessions() const + { return mPossessions; } + + /** + * Gets a reference to the possessions. + */ + Possessions &getPossessions() + { return mPossessions; } + + /** + * Gets the Trade object the character is involved in. + */ + Trade *getTrading() const; + + /** + * Sets the Trade object the character is involved in. + * Cancels other transactions. + */ + void setTrading(Trade *t); + + /** + * Gets the BuySell object the character is involved in. + */ + BuySell *getBuySell() const; + + /** + * Sets the trade object the character is involved in. + * Cancels other transactions. + */ + void setBuySell(BuySell *t); + + /** + * Cancels current transaction. + */ + void cancelTransaction(); + + /** + * Gets transaction status of the character. + */ + bool isBusy() const + { return mTransaction != TRANS_NONE; } + + /* + * Character data: + * Get and set methods + * Most of this should be accessed directly as a friend + */ + + int getDatabaseID() const { return mDatabaseID; } + void setDatabaseID(int id) { mDatabaseID = id; } + + int getHairStyle() const { return mHairStyle; } + void setHairStyle(int style) { mHairStyle = style; } + + int getHairColor() const { return mHairColor; } + void setHairColor(int color) { mHairColor = color; } + + int getAccountLevel() const { return mAccountLevel; } + void setAccountLevel(int l) { mAccountLevel = l; } + + /** Gets the party id of the character */ + int getParty() const + { return mParty; } + + /** Sets the party id of the character */ + void setParty(int party) + { mParty = party; } + + /** + * Sends a message that informs the client about attribute + * modified since last call. + */ + void sendStatus(Entity &entity); + + /** + * Marks all attributes as being modified. + */ + void modifiedAllAttributes(Entity &entity); + + /** + * 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 attributeChanged(Entity *being, AttributeInfo *); + + /** + * Calls all the "disconnected" listener. + */ + void disconnected(Entity &entity); + + /** + * Associative array containing all the quest variables known by the + * server. + */ + std::map< std::string, std::string > questCache; + + /** + * Used to serialize kill count. + */ + int getKillCountSize() const + { return mKillCount.size(); } + + const std::map::const_iterator getKillCountBegin() const + { return mKillCount.begin(); } + + const std::map::const_iterator getKillCountEnd() const + { return mKillCount.end(); } + + void setKillCount(int monsterId, int kills) + { mKillCount[monsterId] = kills; } + + /** + * Adds one kill of the monster type to the characters kill count. + */ + void incrementKillCount(int monsterType); + + /** + * Gets the number of monsters the character killed of a given type. + */ + int getKillCount(int monsterType) const; + + /** + * Tries to use a character point to increase a + * basic attribute + */ + AttribmodResponseCode useCharacterPoint(Entity &entity, + AttributeInfo *); + + /** + * Tries to use a correction point to reduce a + * basic attribute and regain a character point + */ + AttribmodResponseCode useCorrectionPoint(Entity &entity, + AttributeInfo *); + + void setAttributePoints(int points); + int getAttributePoints() const; + + void setCorrectionPoints(int points); + int getCorrectionPoints() const; + + + /** + * Starts the given NPC thread. + * + * Should be called immediately after creating the thread and pushing + * the NPC function and its parameters. + */ + void startNpcThread(Script::Thread *thread, int npcId); + + /** + * Resumes the given NPC thread of this character and sends the NPC + * close message to the player when the script is done. + * + * Should be called after preparing the current Script instance for + * resuming the thread and pushing the parameters the script expects. + */ + void resumeNpcThread(); + + /** + * Returns the NPC thread in use by this character, if any. + */ + Script::Thread *getNpcThread() const + { return mNpcThread; } + + /** Makes it impossible to chat for a while */ + void mute(int seconds) + { mMuteTimeout.set(seconds * 10); } + + bool isMuted() const + { return !mMuteTimeout.expired(); } + + bool isConnected() const + { return mConnected; } + + static void setDeathCallback(Script *script) + { script->assignCallback(mDeathCallback); } + + static void setDeathAcceptedCallback(Script *script) + { script->assignCallback(mDeathAcceptedCallback); } + + static void setLoginCallback(Script *script) + { script->assignCallback(mLoginCallback); } + + void triggerLoginCallback(Entity &entity); + + sigc::signal signal_disconnected; + + void serialize(Entity &entity, MessageOut &msg); + + private: + void deserialize(Entity &entity, MessageIn &msg); + + double getAttrBase(AttributeMap::const_iterator it) const + { return it->second.getBase(); } + double getAttrMod(AttributeMap::const_iterator it) const + { return it->second.getModifiedAttribute(); } + + CharacterComponent(const CharacterComponent &); + CharacterComponent &operator=(const CharacterComponent &); + + void abilityStatusChanged(int id); + void abilityCooldownActivated(); + + void sendAbilityUpdate(Entity &entity); + void sendAbilityCooldownUpdate(Entity &entity); + void sendAttributePointsStatus(Entity &entity); + + enum TransactionType + { TRANS_NONE, TRANS_TRADE, TRANS_BUYSELL }; + + GameClient *mClient; /**< Client computer. */ + + /** + * Tells whether the character client is connected. + * Useful when dealing with enqueued events. + */ + bool mConnected; + + /** Handler of the transaction the character is involved in. */ + void *mTransactionHandler; + + Possessions mPossessions; /**< Possesssions of the character. */ + + /** Attributes modified since last update. */ + std::set mModifiedAttributes; + + std::set mModifiedAbilities; + + int mDatabaseID; /**< Character's database ID. */ + unsigned char mHairStyle; /**< Hair Style of the character. */ + unsigned char mHairColor; /**< Hair Color of the character. */ + + bool mSendAttributePointsStatus; + int mAttributePoints; /**< Unused attribute points that can be distributed */ + int mCorrectionPoints; /**< Unused attribute correction points */ + + bool mSendAbilityCooldown; + unsigned char mAccountLevel; /**< Account level of the user. */ + int mParty; /**< Party id of the character */ + TransactionType mTransaction; /**< Trade/buy/sell action the character is involved in. */ + std::map mKillCount; /**< How many monsters the character has slain of each type */ + + int mTalkNpcId; /**< Public ID of NPC the character is talking to, if any */ + Script::Thread *mNpcThread; /**< Script thread executing NPC interaction, if any */ + + Timeout mMuteTimeout; /**< Time until the character is no longer muted */ + + 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; +}; + + +inline void CharacterComponent::setAttributePoints(int points) +{ + mSendAttributePointsStatus = true; + mAttributePoints = points; +} + +inline int CharacterComponent::getAttributePoints() const +{ + return mAttributePoints; +} + +inline void CharacterComponent::setCorrectionPoints(int points) +{ + mSendAttributePointsStatus = true; + mCorrectionPoints = points; +} + +inline int CharacterComponent::getCorrectionPoints() const +{ + return mCorrectionPoints; +} + +#endif // CHARACTER_H diff --git a/src/game-server/commandhandler.cpp b/src/game-server/commandhandler.cpp index 79758bcf..44559a5e 100644 --- a/src/game-server/commandhandler.cpp +++ b/src/game-server/commandhandler.cpp @@ -22,7 +22,7 @@ #include "game-server/commandhandler.h" #include "game-server/accountconnection.h" -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "game-server/effect.h" #include "game-server/gamehandler.h" #include "game-server/inventory.h" diff --git a/src/game-server/inventory.h b/src/game-server/inventory.h index 949471b6..627d0c97 100644 --- a/src/game-server/inventory.h +++ b/src/game-server/inventory.h @@ -21,7 +21,7 @@ #ifndef INVENTORY_H #define INVENTORY_H -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "net/messageout.h" class ItemClass; diff --git a/src/game-server/mapcomposite.cpp b/src/game-server/mapcomposite.cpp index 83c63fa3..17e7667f 100644 --- a/src/game-server/mapcomposite.cpp +++ b/src/game-server/mapcomposite.cpp @@ -25,7 +25,7 @@ #include "accountconnection.h" #include "common/configuration.h" #include "common/resourcemanager.h" -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "game-server/mapcomposite.h" #include "game-server/map.h" #include "game-server/mapmanager.h" diff --git a/src/game-server/monster.cpp b/src/game-server/monster.cpp index cf589977..56a08cd9 100644 --- a/src/game-server/monster.cpp +++ b/src/game-server/monster.cpp @@ -23,7 +23,7 @@ #include "common/configuration.h" #include "common/resourcemanager.h" #include "game-server/attributemanager.h" -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "game-server/collisiondetection.h" #include "game-server/item.h" #include "game-server/map.h" diff --git a/src/game-server/npc.cpp b/src/game-server/npc.cpp index 980358da..2b6ab1fd 100644 --- a/src/game-server/npc.cpp +++ b/src/game-server/npc.cpp @@ -21,7 +21,7 @@ #include "game-server/npc.h" -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "game-server/gamehandler.h" #include "game-server/map.h" #include "net/messageout.h" diff --git a/src/game-server/postman.h b/src/game-server/postman.h index 145db9e7..a8d82770 100644 --- a/src/game-server/postman.h +++ b/src/game-server/postman.h @@ -24,7 +24,7 @@ #include #include -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "game-server/entity.h" class Script; diff --git a/src/game-server/quest.cpp b/src/game-server/quest.cpp index 515e6200..4ed8a6fc 100644 --- a/src/game-server/quest.cpp +++ b/src/game-server/quest.cpp @@ -21,7 +21,7 @@ #include "game-server/quest.h" #include "game-server/accountconnection.h" -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "utils/logger.h" #include diff --git a/src/game-server/trade.cpp b/src/game-server/trade.cpp index b30eaf79..57bc04e6 100644 --- a/src/game-server/trade.cpp +++ b/src/game-server/trade.cpp @@ -23,7 +23,7 @@ #include "game-server/trade.h" -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "game-server/gamehandler.h" #include "game-server/inventory.h" #include "net/messageout.h" diff --git a/src/game-server/triggerareacomponent.cpp b/src/game-server/triggerareacomponent.cpp index 04e77b6a..dcd4dd1a 100644 --- a/src/game-server/triggerareacomponent.cpp +++ b/src/game-server/triggerareacomponent.cpp @@ -21,7 +21,7 @@ #include "game-server/triggerareacomponent.h" -#include "game-server/character.h" +#include "game-server/charactercomponent.h" #include "game-server/mapcomposite.h" #include "game-server/actor.h" #include "game-server/state.h" -- cgit v1.2.3-60-g2f50