diff options
author | Erik Schilling <ablu.erikschilling@googlemail.com> | 2013-08-19 22:15:26 +0200 |
---|---|---|
committer | Erik Schilling <ablu.erikschilling@googlemail.com> | 2013-08-26 22:56:48 +0200 |
commit | 81a88f1dd199691ce570ab124a43740b77a67f03 (patch) | |
tree | e5cd82a8b73c1d909f63fb18357daac91f7c0d62 | |
parent | b379c150cedfbae4775c8358369ec590ad4033f4 (diff) | |
download | manaserv-81a88f1dd199691ce570ab124a43740b77a67f03.tar.gz manaserv-81a88f1dd199691ce570ab124a43740b77a67f03.tar.bz2 manaserv-81a88f1dd199691ce570ab124a43740b77a67f03.tar.xz manaserv-81a88f1dd199691ce570ab124a43740b77a67f03.zip |
Allowed to push attributeinfos for the attribute recalculation
While on it I replaced the id usage in the server with the usage of the
AttributeInfo directly.
Next steps:
- Dehardcode the core attribute ids and store their
attributeinfos somewhere in AttributeManager (for now i simply
wrapped the ids with getAttributeInfo().
- Move AttributeInfo out of AttributeManager to shorten the usage + to
allow using a pointer in ModifierLocation without forward declaring
it.
31 files changed, 384 insertions, 285 deletions
diff --git a/example/scripts/abilities.lua b/example/scripts/abilities.lua index cbdc22d3..46d97af8 100644 --- a/example/scripts/abilities.lua +++ b/example/scripts/abilities.lua @@ -28,7 +28,7 @@ spell1:on_use(function(user, x, y, abilityId) local damage = { base = 10, delta = 5, - chance_to_hit = user:modified_attribute(ATTR_STR), + chance_to_hit = user:modified_attribute("Strength"), } being:damage(user, damage) being:say("OUCH") diff --git a/example/scripts/attributes.lua b/example/scripts/attributes.lua index 3e9b97e0..7a3f6098 100644 --- a/example/scripts/attributes.lua +++ b/example/scripts/attributes.lua @@ -7,78 +7,76 @@ --]] -local ATTR_EXP = 22 -local ATTR_LEVEL = 23 - -local function recalculate_base_attribute(being, attribute) - local old_base = being:base_attribute(attribute) +local function recalculate_base_attribute(being, attribute_name) + local old_base = being:base_attribute(attribute_name) local new_base = old_base - if attribute == ATTR_ACCURACY then + if attribute == "Accuracy" then -- Provisional - new_base = being:modified_attribute(ATTR_DEX) - elseif attribute == ATTR_DEFENSE then - new_base = 0.3 * being:modified_attribute(ATTR_VIT) - elseif attribute == ATTR_DODGE then + new_base = being:modified_attribute("Dexterity") + elseif attribute == "Defense" then + new_base = 0.3 * being:modified_attribute("Vitality") + elseif attribute == "Dodge" then -- Provisional - new_base = being:modified_attribute(ATTR_AGI) - elseif attribute == ATTR_MAGIC_DODGE then + new_base = being:modified_attribute("Agility") + elseif attribute == "M. dodge" then -- TODO new_base = 1 - elseif attribute == ATTR_MAGIC_DEFENSE then + elseif attribute == "M. defense" then -- TODO new_base = 0 - elseif attribute == ATTR_BONUS_ASPD then + elseif attribute == "Bonus att. speed" then -- TODO new_base = 0 - elseif attribute == ATTR_HP_REGEN then - local hp_per_sec = being:modified_attribute(ATTR_VIT) * 0.05 + elseif attribute == "HP regeneration" then + local hp_per_sec = being:modified_attribute("Vitality") * 0.05 new_base = hp_per_sec * TICKS_PER_HP_REGENERATION / 10 - elseif attribute == ATTR_HP then - local hp = being:modified_attribute(ATTR_HP) - local max_hp = being:modified_attribute(ATTR_MAX_HP) + elseif attribute == "HP" then + local hp = being:modified_attribute("HP") + local max_hp = being:modified_attribute("Max HP") if hp > max_hp then new_base = new_base - hp - max_hp end - elseif attribute == ATTR_MAX_HP then - local vit = being:modified_attribute(ATTR_VIT) + elseif attribute == "Max HP" then + local vit = being:modified_attribute("Vitality") new_base = (vit + 3) * (vit + 20) * 0.125 - elseif attribute == ATTR_MOVE_SPEED_TPS then + elseif attribute == "Movement speed" then -- Provisional - new_base = 3.0 + being:modified_attribute(ATTR_AGI) * 0.08 - elseif attribute == ATTR_INV_CAPACITY then + new_base = 3.0 + being:modified_attribute("Agility") * 0.08 + elseif attribute == "Capacity" then -- Provisional - new_base = 2000 + being:modified_attribute(ATTR_STR) * 180 - elseif attribute == ATTR_ABILITY_COOLDOWN then + new_base = 2000 + being:modified_attribute("Strength") * 180 + elseif attribute == "Global ability cooldown" then -- Provisional - new_base = 100 - being:modified_attribute(ATTR_WIL) - elseif attribute == ATTR_LEVEL then + new_base = 100 - being:modified_attribute("Willpower") + elseif attribute == "Level" then -- Provisional - --new_base = 100 - 100 * math.pow(0.99999, being:base_attribute(ATTR_EXP)) - new_base = being:base_attribute(ATTR_EXP) / 20 + --new_base = 100 - 100 * math.pow(0.99999, being:base_attribute("XP")) + new_base = being:base_attribute("XP") / 20 end if new_base ~= old_base then - being:set_base_attribute(attribute, new_base) + being:set_base_attribute(attribute_name, new_base) end end local function update_derived_attributes(being, attribute) - if attribute == ATTR_STR then - recalculate_base_attribute(being, ATTR_INV_CAPACITY) - elseif attribute == ATTR_AGI then - recalculate_base_attribute(being, ATTR_DODGE) - elseif attribute == ATTR_VIT then - recalculate_base_attribute(being, ATTR_MAX_HP) - recalculate_base_attribute(being, ATTR_HP_REGEN) - recalculate_base_attribute(being, ATTR_DEFENSE) - elseif attribute == ATTR_INT then + local attribute_name = attribute:name() + if attribute_name == "Strength" then + recalculate_base_attribute(being, "Capacity") + elseif attribute_name == "Agility" then + recalculate_base_attribute(being, "Dodge") + elseif attribute_name == "Vitality" then + recalculate_base_attribute(being, "Max HP") + recalculate_base_attribute(being, "HP regeneration") + recalculate_base_attribute(being, "Defense") + elseif attribute_name == "Intelligence" then -- unimplemented - elseif attribute == ATTR_WIL then - recalculate_base_attribute(being, ATTR_ABILITY_COOLDOWN) - elseif attribute == ATTR_EXP then - recalculate_base_attribute(being, ATTR_LEVEL) + elseif attribute_name == "Willpower" then + recalculate_base_attribute(being, "Global ability cooldown") + elseif attribute_name == "XP" then + recalculate_base_attribute(being, "Level") end end @@ -86,13 +84,13 @@ on_recalculate_base_attribute(recalculate_base_attribute) on_update_derived_attribute(update_derived_attributes) function Entity:level() - return math.floor(self:base_attribute(ATTR_LEVEL)) + return math.floor(self:base_attribute("Level")) end function Entity:give_experience(experience) - local old_experience = self:base_attribute(ATTR_EXP) + local old_experience = self:base_attribute("XP") local old_level = self:level() - self:set_base_attribute(ATTR_EXP, old_experience + experience) + self:set_base_attribute("XP", old_experience + experience) if self:level() > old_level then self:say("LEVELUP!!! " .. self:level()) self:set_attribute_points(self:attribute_points() + 1) @@ -128,7 +126,7 @@ local function monster_damaged(mob, source, damage) receiver.total = receiver.total + damage end - if mob:base_attribute(ATTR_HP) == 0 then + if mob:base_attribute("HP") == 0 then local mob_config = mobs_config[mob:name()] local experience = mob_config.experience or 0 for char, damage in pairs(receiver.chars) do diff --git a/example/scripts/damage.lua b/example/scripts/damage.lua index 8ad014f4..0116fbec 100644 --- a/example/scripts/damage.lua +++ b/example/scripts/damage.lua @@ -14,7 +14,7 @@ end -- base, delta, chance_to_hit function Entity:damage(source, damage) local hp_loss = math.random(damage.base, damage.base + damage.delta) - local dodge = self:modified_attribute(ATTR_DODGE) + local dodge = self:modified_attribute("Dodge") if dodge > 0 and math.random(dodge) > math.random(damage.chance_to_hit) or damage.chance_to_hit == 0 @@ -22,7 +22,7 @@ function Entity:damage(source, damage) hp_loss = 0 -- attack missed self:say("HAHA MISSED") else - local defense = self:modified_attribute(ATTR_DEFENSE) + local defense = self:modified_attribute("Defense") local randomness = hp_loss > 16 and math.random(hp_loss / 16) or 0 hp_loss = hp_loss * (1 - (0.0159375 * defense) / (1 + 0.017 * defense)) + randomness @@ -30,10 +30,10 @@ function Entity:damage(source, damage) end if hp_loss > 0 then - local hp = self:base_attribute(ATTR_HP) + local hp = self:base_attribute("HP") hp_loss = math.min(hp, hp_loss) self:add_hit_taken(hp_loss) - self:set_base_attribute(ATTR_HP, hp - hp_loss) + self:set_base_attribute("HP", hp - hp_loss) self:say("I GOT DAMAGED " .. hp - hp_loss) if self:type() == TYPE_MONSTER then diff --git a/example/scripts/npcs/postman.lua b/example/scripts/npcs/postman.lua index 46802283..2ad2e3ce 100644 --- a/example/scripts/npcs/postman.lua +++ b/example/scripts/npcs/postman.lua @@ -12,7 +12,7 @@ function post_talk(npc, ch) say("Hello " .. ch:name()) - local strength = being_get_attribute(ch, ATTR_STRENGTH) + local strength = being_get_attribute(ch, "Strength"ENGTH) say("You have " .. tostring(strength) .. " strength") say("What would you like to do?") local answer = ask("View Mail", "Send Mail", "Nothing") diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp index eed02b89..604fe2ea 100644 --- a/src/game-server/accountconnection.cpp +++ b/src/game-server/accountconnection.cpp @@ -464,7 +464,7 @@ void AccountConnection::updateCharacterPoints(int charId, int charPoints, } void AccountConnection::updateAttributes(int charId, int attrId, double base, - double mod) + double mod) { ++mSyncMessages; mSyncBuffer->writeInt8(SYNC_CHARACTER_ATTRIBUTE); diff --git a/src/game-server/attributemanager.cpp b/src/game-server/attributemanager.cpp index 9ed76640..3eb36cb8 100644 --- a/src/game-server/attributemanager.cpp +++ b/src/game-server/attributemanager.cpp @@ -47,7 +47,7 @@ void AttributeManager::deinitialize() mAttributeScopes[i].clear(); } -const AttributeManager::AttributeInfo *AttributeManager::getAttributeInfo( +AttributeManager::AttributeInfo *AttributeManager::getAttributeInfo( int id) const { auto ret = mAttributeMap.find(id); @@ -56,7 +56,7 @@ const AttributeManager::AttributeInfo *AttributeManager::getAttributeInfo( return ret->second; } -const AttributeManager::AttributeInfo *AttributeManager::getAttributeInfo( +AttributeManager::AttributeInfo *AttributeManager::getAttributeInfo( const std::string &name) const { if (mAttributeNameMap.contains(name)) @@ -64,20 +64,12 @@ const AttributeManager::AttributeInfo *AttributeManager::getAttributeInfo( return 0; } -const std::map<int, AttributeManager::AttributeInfo *> +const std::set<AttributeManager::AttributeInfo *> &AttributeManager::getAttributeScope(ScopeType type) const { return mAttributeScopes[type]; } -bool AttributeManager::isAttributeDirectlyModifiable(int id) const -{ - auto ret = mAttributeMap.find(id); - if (ret == mAttributeMap.end()) - return false; - return ret->second->modifiable; -} - ModifierLocation AttributeManager::getLocation(const std::string &tag) const { if (mTagMap.find(tag) != mTagMap.end()) @@ -111,9 +103,16 @@ void AttributeManager::readAttributeNode(xmlNodePtr attributeNode) return; } - AttributeInfo *attribute = new AttributeInfo; + std::string name = XML::getProperty(attributeNode, "name", std::string()); + if (name.empty()) + { + LOG_WARN("Attribute manager: attribute '" << id + << "' does not have a name! Skipping..."); + return; + } + + AttributeInfo *attribute = new AttributeInfo(id, name); - attribute->id = id; attribute->modifiers = std::vector<AttributeModifier>(); attribute->minimum = XML::getFloatProperty(attributeNode, "minimum", std::numeric_limits<double>::min()); @@ -122,15 +121,6 @@ void AttributeManager::readAttributeNode(xmlNodePtr attributeNode) attribute->modifiable = XML::getBoolProperty(attributeNode, "modifiable", false); - std::string name = XML::getProperty(attributeNode, "name", std::string()); - if (name.empty()) - { - LOG_WARN("Attribute manager: attribute '" << id - << "' does not have a name! Skipping..."); - delete attribute; - return; - } - const std::string scope = utils::toUpper( XML::getProperty(attributeNode, "scope", std::string())); @@ -138,21 +128,21 @@ void AttributeManager::readAttributeNode(xmlNodePtr attributeNode) if (scope.find("CHARACTER") != std::string::npos) { - mAttributeScopes[CharacterScope][id] = attribute; + mAttributeScopes[CharacterScope].insert(attribute); LOG_DEBUG("Attribute manager: attribute '" << id << "' added to default character scope."); hasScope = true; } if (scope.find("MONSTER") != std::string::npos) { - mAttributeScopes[MonsterScope][id] = attribute; + mAttributeScopes[MonsterScope].insert(attribute); LOG_DEBUG("Attribute manager: attribute '" << id << "' added to default monster scope."); hasScope = true; } if (scope == "BEING") { - mAttributeScopes[BeingScope][id] = attribute; + mAttributeScopes[BeingScope].insert(attribute); LOG_DEBUG("Attribute manager: attribute '" << id << "' added to default being scope."); hasScope = true; diff --git a/src/game-server/attributemanager.h b/src/game-server/attributemanager.h index 2972cd77..c6782130 100644 --- a/src/game-server/attributemanager.h +++ b/src/game-server/attributemanager.h @@ -21,10 +21,11 @@ #ifndef ATTRIBUTEMANAGER_H #define ATTRIBUTEMANAGER_H +#include <limits> #include <map> -#include <vector> +#include <set> #include <string> -#include <limits> +#include <vector> #include "utils/string.h" #include "utils/xml.h" @@ -91,14 +92,16 @@ class AttributeManager { public: struct AttributeInfo { - AttributeInfo(): - id(0), + AttributeInfo(int id, const std::string &name): + id(id), + name(name), minimum(std::numeric_limits<double>::min()), maximum(std::numeric_limits<double>::max()), modifiable(false) {} int id; + std::string name; /** The minimum and maximum permitted attribute values. */ double minimum; @@ -123,12 +126,10 @@ class AttributeManager void reload(); void deinitialize(); - const AttributeInfo *getAttributeInfo(int id) const; - const AttributeInfo *getAttributeInfo(const std::string &name) const; - - const std::map<int, AttributeInfo *> &getAttributeScope(ScopeType) const; + AttributeInfo *getAttributeInfo(int id) const; + AttributeInfo *getAttributeInfo(const std::string &name) const; - bool isAttributeDirectlyModifiable(int id) const; + const std::set<AttributeInfo *> &getAttributeScope(ScopeType) const; ModifierLocation getLocation(const std::string &tag) const; @@ -142,7 +143,7 @@ class AttributeManager void readModifierNode(xmlNodePtr modifierNode, int attributeId, AttributeInfo *info); - std::map<int, AttributeInfo *> mAttributeScopes[MaxScope]; + std::set<AttributeInfo *> mAttributeScopes[MaxScope]; std::map<int, AttributeInfo *> mAttributeMap; utils::NameMap<AttributeInfo *> mAttributeNameMap; diff --git a/src/game-server/being.cpp b/src/game-server/being.cpp index 9b45608c..205a5778 100644 --- a/src/game-server/being.cpp +++ b/src/game-server/being.cpp @@ -49,18 +49,11 @@ BeingComponent::BeingComponent(Entity &entity): auto &attributeScope = attributeManager->getAttributeScope(BeingScope); LOG_DEBUG("Being creation: initialisation of " << attributeScope.size() << " attributes."); - for (auto &attributeIt : attributeScope) + for (auto &attribute : attributeScope) { - if (mAttributes.count(attributeIt.first)) - { - LOG_WARN("Redefinition of attribute '" - << attributeIt.first << "'!"); - } - LOG_DEBUG("Attempting to create attribute '" - << attributeIt.first << "'."); - mAttributes.insert(std::make_pair(attributeIt.first, - Attribute(attributeIt.second))); + << attribute->id << "'."); + mAttributes.insert(std::make_pair(attribute, Attribute(attribute))); } clearDestination(entity); @@ -93,27 +86,30 @@ void BeingComponent::triggerEmote(Entity &entity, int id) void BeingComponent::heal(Entity &entity) { - Attribute &hp = mAttributes.at(ATTR_HP); - Attribute &maxHp = mAttributes.at(ATTR_MAX_HP); + auto *hpAttribute = attributeManager->getAttributeInfo(ATTR_HP); + Attribute &hp = mAttributes.at(hpAttribute); + Attribute &maxHp = mAttributes.at(attributeManager->getAttributeInfo(ATTR_MAX_HP)); if (maxHp.getModifiedAttribute() == hp.getModifiedAttribute()) return; // Full hp, do nothing. // Reset all modifications present in hp. hp.clearMods(); - setAttribute(entity, ATTR_HP, maxHp.getModifiedAttribute()); + setAttribute(entity, hpAttribute, maxHp.getModifiedAttribute()); } void BeingComponent::heal(Entity &entity, int gain) { - Attribute &hp = mAttributes.at(ATTR_HP); - Attribute &maxHp = mAttributes.at(ATTR_MAX_HP); + auto *hpAttribute = attributeManager->getAttributeInfo(ATTR_HP); + auto *maxHpAttribute = attributeManager->getAttributeInfo(ATTR_MAX_HP); + Attribute &hp = mAttributes.at(hpAttribute); + Attribute &maxHp = mAttributes.at(maxHpAttribute); if (maxHp.getModifiedAttribute() == hp.getModifiedAttribute()) return; // Full hp, do nothing. // Cannot go over maximum hitpoints. - setAttribute(entity, ATTR_HP, hp.getBase() + gain); + setAttribute(entity, hpAttribute, hp.getBase() + gain); if (hp.getModifiedAttribute() > maxHp.getModifiedAttribute()) - setAttribute(entity, ATTR_HP, maxHp.getModifiedAttribute()); + setAttribute(entity, hpAttribute, maxHp.getModifiedAttribute()); } void BeingComponent::died(Entity &entity) @@ -188,8 +184,8 @@ void BeingComponent::updateDirection(Entity &entity, void BeingComponent::move(Entity &entity) { // Immobile beings cannot move. - if (!checkAttributeExists(ATTR_MOVE_SPEED_RAW) - || !getModifiedAttribute(ATTR_MOVE_SPEED_RAW)) + if (!checkAttributeExists(attributeManager->getAttributeInfo(ATTR_MOVE_SPEED_RAW)) + || !getModifiedAttribute(attributeManager->getAttributeInfo(ATTR_MOVE_SPEED_RAW))) return; // Remember the current position before moving. This is used by @@ -271,10 +267,12 @@ void BeingComponent::move(Entity &entity) { Point next = mPath.front(); mPath.pop_front(); + + auto *rawSpeedAttribute = attributeManager->getAttributeInfo(ATTR_MOVE_SPEED_RAW); // SQRT2 is used for diagonal movement. mMoveTime += (prev.x == next.x || prev.y == next.y) ? - getModifiedAttribute(ATTR_MOVE_SPEED_RAW) : - getModifiedAttribute(ATTR_MOVE_SPEED_RAW) * SQRT2; + getModifiedAttribute(rawSpeedAttribute) : + getModifiedAttribute(rawSpeedAttribute) * SQRT2; if (mPath.empty()) { @@ -319,20 +317,20 @@ void BeingComponent::setAction(Entity &entity, BeingAction action) } } -void BeingComponent::applyModifier(Entity &entity, unsigned attr, double value, - unsigned layer, unsigned duration, - unsigned id) +void BeingComponent::applyModifier(Entity &entity, AttributeManager::AttributeInfo *attribute, + double value, unsigned layer, + unsigned duration, unsigned id) { - mAttributes.at(attr).add(duration, value, layer, id); - updateDerivedAttributes(entity, attr); + mAttributes.at(attribute).add(duration, value, layer, id); + updateDerivedAttributes(entity, attribute); } -bool BeingComponent::removeModifier(Entity &entity, unsigned attr, +bool BeingComponent::removeModifier(Entity &entity, AttributeManager::AttributeInfo *attribute, double value, unsigned layer, unsigned id, bool fullcheck) { - bool ret = mAttributes.at(attr).remove(value, layer, id, fullcheck); - updateDerivedAttributes(entity, attr); + bool ret = mAttributes.at(attribute).remove(value, layer, id, fullcheck); + updateDerivedAttributes(entity, attribute); return ret; } @@ -341,87 +339,90 @@ void BeingComponent::setGender(BeingGender gender) mGender = gender; } -void BeingComponent::setAttribute(Entity &entity, unsigned id, double value) +void BeingComponent::setAttribute(Entity &entity, + AttributeManager::AttributeInfo *attribute, + double value) { - auto attributeIt = mAttributes.find(id); + auto attributeIt = mAttributes.find(attribute); if (attributeIt == mAttributes.end()) { /* * The attribute does not yet exist, so we must attempt to create it. */ LOG_ERROR("Being: Attempt to access non-existing attribute '" - << id << "'!"); + << attribute->id << "'!"); LOG_WARN("Being: Creation of new attributes dynamically is not " "implemented yet!"); } else { attributeIt->second.setBase(value); - updateDerivedAttributes(entity, id); + updateDerivedAttributes(entity, attribute); } } -void BeingComponent::createAttribute(unsigned id, - const AttributeManager::AttributeInfo *attributeInfo) +void BeingComponent::createAttribute(AttributeManager::AttributeInfo *attributeInfo) { - mAttributes.insert(std::pair<unsigned, Attribute> - (id, Attribute(attributeInfo))); + mAttributes.insert(std::pair<AttributeManager::AttributeInfo *, Attribute> + (attributeInfo, Attribute(attributeInfo))); } -const Attribute *BeingComponent::getAttribute(unsigned id) const +const Attribute *BeingComponent::getAttribute(AttributeManager::AttributeInfo *attribute) const { - AttributeMap::const_iterator ret = mAttributes.find(id); + AttributeMap::const_iterator ret = mAttributes.find(attribute); if (ret == mAttributes.end()) { LOG_DEBUG("BeingComponent::getAttribute: Attribute " - << id << " not found! Returning 0."); + << attribute->id << " not found! Returning 0."); return 0; } return &ret->second; } -double BeingComponent::getAttributeBase(unsigned id) const +double BeingComponent::getAttributeBase(AttributeManager::AttributeInfo *attribute) const { - AttributeMap::const_iterator ret = mAttributes.find(id); + AttributeMap::const_iterator ret = mAttributes.find(attribute); if (ret == mAttributes.end()) { LOG_DEBUG("BeingComponent::getAttributeBase: Attribute " - << id << " not found! Returning 0."); + << attribute->id << " not found! Returning 0."); return 0; } return ret->second.getBase(); } -double BeingComponent::getModifiedAttribute(unsigned id) const +double BeingComponent::getModifiedAttribute(AttributeManager::AttributeInfo *attribute) const { - AttributeMap::const_iterator ret = mAttributes.find(id); + AttributeMap::const_iterator ret = mAttributes.find(attribute); if (ret == mAttributes.end()) { LOG_DEBUG("BeingComponent::getModifiedAttribute: Attribute " - << id << " not found! Returning 0."); + << attribute->id << " not found! Returning 0."); return 0; } return ret->second.getModifiedAttribute(); } -void BeingComponent::recalculateBaseAttribute(Entity &entity, unsigned attr) +void BeingComponent::recalculateBaseAttribute(Entity &entity, + AttributeManager::AttributeInfo *attribute) { LOG_DEBUG("Being: Received update attribute recalculation request for " - << attr << "."); - if (!mAttributes.count(attr)) + << attribute << "."); + if (!mAttributes.count(attribute)) { - LOG_DEBUG("BeingComponent::recalculateBaseAttribute: " << attr << " not found!"); + LOG_DEBUG("BeingComponent::recalculateBaseAttribute: " << attribute->id << " not found!"); return; } // Handle speed conversion inside the engine - if (attr == ATTR_MOVE_SPEED_RAW) + if (attribute == attributeManager->getAttributeInfo(ATTR_MOVE_SPEED_RAW)) { + auto *speedTpsAttribute = attributeManager->getAttributeInfo(ATTR_MOVE_SPEED_TPS); double newBase = utils::tpsToRawSpeed( - getModifiedAttribute(ATTR_MOVE_SPEED_TPS)); - if (newBase != getAttributeBase(attr)) - setAttribute(entity, attr, newBase); + getModifiedAttribute(speedTpsAttribute)); + if (newBase != getAttributeBase(attribute)) + setAttribute(entity, attribute, newBase); return; } @@ -431,18 +432,19 @@ void BeingComponent::recalculateBaseAttribute(Entity &entity, unsigned attr) Script *script = ScriptManager::currentState(); script->prepare(mRecalculateBaseAttributeCallback); script->push(&entity); - script->push(attr); + script->push(attribute); script->execute(entity.getMap()); } -void BeingComponent::updateDerivedAttributes(Entity &entity, unsigned attr) +void BeingComponent::updateDerivedAttributes(Entity &entity, + AttributeManager::AttributeInfo *attribute) { - signal_attribute_changed.emit(&entity, attr); + signal_attribute_changed.emit(&entity, attribute); - LOG_DEBUG("Being: Updating derived attribute(s) of: " << attr); + LOG_DEBUG("Being: Updating derived attribute(s) of: " << attribute); // Handle default actions before handing over to the script engine - switch (attr) + switch (attribute->id) { case ATTR_MAX_HP: case ATTR_HP: @@ -452,7 +454,8 @@ void BeingComponent::updateDerivedAttributes(Entity &entity, unsigned attr) case ATTR_MOVE_SPEED_TPS: // Does not make a lot of sense to have in the scripts. // So handle it here: - recalculateBaseAttribute(entity, ATTR_MOVE_SPEED_RAW); + recalculateBaseAttribute(entity, + attributeManager->getAttributeInfo(ATTR_MOVE_SPEED_RAW)); break; } @@ -462,7 +465,7 @@ void BeingComponent::updateDerivedAttributes(Entity &entity, unsigned attr) Script *script = ScriptManager::currentState(); script->prepare(mRecalculateDerivedAttributesCallback); script->push(&entity); - script->push(attr); + script->push(attribute); script->execute(entity.getMap()); } @@ -514,15 +517,17 @@ void BeingComponent::setStatusEffectTime(int id, int time) void BeingComponent::update(Entity &entity) { - int oldHP = getModifiedAttribute(ATTR_HP); + auto *hpAttribute = attributeManager->getAttributeInfo(ATTR_HP); + + int oldHP = getModifiedAttribute(hpAttribute); int newHP = oldHP; - int maxHP = getModifiedAttribute(ATTR_MAX_HP); + int maxHP = getModifiedAttribute(attributeManager->getAttributeInfo(ATTR_MAX_HP)); // Regenerate HP if (mAction != DEAD && mHealthRegenerationTimeout.expired()) { mHealthRegenerationTimeout.set(TICKS_PER_HP_REGENERATION); - newHP += getModifiedAttribute(ATTR_HP_REGEN); + newHP += getModifiedAttribute(attributeManager->getAttributeInfo(ATTR_HP_REGEN)); } // Cap HP at maximum if (newHP > maxHP) @@ -532,7 +537,7 @@ void BeingComponent::update(Entity &entity) // Only update HP when it actually changed to avoid network noise if (newHP != oldHP) { - setAttribute(entity, ATTR_HP, newHP); + setAttribute(entity, hpAttribute, newHP); entity.getComponent<ActorComponent>()->raiseUpdateFlags( UPDATEFLAG_HEALTHCHANGE); } @@ -567,7 +572,7 @@ void BeingComponent::update(Entity &entity) } // Check if being died - if (getModifiedAttribute(ATTR_HP) <= 0 && mAction != DEAD) + if (getModifiedAttribute(hpAttribute) <= 0 && mAction != DEAD) died(entity); } diff --git a/src/game-server/being.h b/src/game-server/being.h index f542e3ab..908def6d 100644 --- a/src/game-server/being.h +++ b/src/game-server/being.h @@ -29,6 +29,7 @@ #include "game-server/actor.h" #include "game-server/attribute.h" +#include "game-server/attributemanager.h" #include "game-server/timeout.h" #include "scripting/script.h" @@ -37,7 +38,7 @@ class BeingComponent; class MapComposite; class StatusEffect; -typedef std::map< unsigned, Attribute > AttributeMap; +typedef std::map<AttributeManager::AttributeInfo *, Attribute> AttributeMap; struct Status { @@ -143,7 +144,7 @@ class BeingComponent : public Component /** * Sets an attribute. */ - void setAttribute(Entity &entity, unsigned id, double value); + void setAttribute(Entity &entity, AttributeManager::AttributeInfo *, double value); /** * Creates an Attribute that did not exist before @@ -151,13 +152,12 @@ class BeingComponent : public Component * @param id The id of the attribute * @param attributeInfo The info that describes the attribute */ - void createAttribute(unsigned id, - const AttributeManager::AttributeInfo *attributeInfo); + void createAttribute(AttributeManager::AttributeInfo *); /** * Gets an attribute or 0 if not existing. */ - const Attribute *getAttribute(unsigned id) const; + const Attribute *getAttribute(AttributeManager::AttributeInfo *) const; const AttributeMap &getAttributes() const { return mAttributes; } @@ -165,20 +165,20 @@ class BeingComponent : public Component /** * Gets an attribute base. */ - double getAttributeBase(unsigned id) const; + double getAttributeBase(AttributeManager::AttributeInfo *) const; /** * Gets an attribute after applying modifiers. */ - double getModifiedAttribute(unsigned id) const; + double getModifiedAttribute(AttributeManager::AttributeInfo *) const; /** * Checks whether or not an attribute exists in this being. * @returns True if the attribute is present in the being, false otherwise. */ - bool checkAttributeExists(unsigned id) const - { return mAttributes.count(id); } + bool checkAttributeExists(AttributeManager::AttributeInfo *attribute) const + { return mAttributes.count(attribute); } /** * Adds a modifier to one attribute. @@ -187,13 +187,13 @@ class BeingComponent : public Component * @param lvl If non-zero, indicates that a temporary modifier can be * dispelled prematuraly by a spell of given level. */ - void applyModifier(Entity &entity, unsigned attr, double value, - unsigned layer, unsigned duration = 0, - unsigned id = 0); + void applyModifier(Entity &entity, AttributeManager::AttributeInfo *, + double value, unsigned layer, + unsigned duration = 0, unsigned id = 0); - bool removeModifier(Entity &entity, unsigned attr, double value, - unsigned layer, unsigned id = 0, - bool fullcheck = false); + bool removeModifier(Entity &entity, AttributeManager::AttributeInfo *, + double value, unsigned layer, + unsigned id = 0, bool fullcheck = false); /** * Called when an attribute modifier is changed. @@ -201,14 +201,16 @@ class BeingComponent : public Component * attributes if it has changed. * @returns Whether it was changed. */ - void recalculateBaseAttribute(Entity &, unsigned); + void recalculateBaseAttribute(Entity &, + AttributeManager::AttributeInfo *); /** * Attribute has changed, recalculate base value of dependant * attributes (and handle other actions for the modified * attribute) */ - void updateDerivedAttributes(Entity &entity, unsigned); + void updateDerivedAttributes(Entity &entity, + AttributeManager::AttributeInfo *); /** * Sets a statuseffect on this being @@ -258,7 +260,7 @@ class BeingComponent : public Component { script->assignCallback(mRecalculateBaseAttributeCallback); } sigc::signal<void, Entity *> signal_died; - sigc::signal<void, Entity *, unsigned> signal_attribute_changed; + sigc::signal<void, Entity *, AttributeManager::AttributeInfo *> signal_attribute_changed; /** * Activate an emote flag on the being. diff --git a/src/game-server/buysell.cpp b/src/game-server/buysell.cpp index cc209ccf..ecc8793c 100644 --- a/src/game-server/buysell.cpp +++ b/src/game-server/buysell.cpp @@ -30,8 +30,10 @@ #include <algorithm> -BuySell::BuySell(Entity *c, bool sell): - mCurrencyId(ATTR_GP), mChar(c), mSell(sell) +BuySell::BuySell(Entity *c, bool sell) + : mCurrency(attributeManager->getAttributeInfo(ATTR_GP)) + , mChar(c) + , mSell(sell) { c->getComponent<CharacterComponent>()->setBuySell(this); } @@ -167,17 +169,17 @@ void BuySell::perform(unsigned id, int amount) { amount -= inv.remove(id, amount); const double currentMoney = - beingComponent->getAttributeBase(mCurrencyId); - beingComponent->setAttribute(*mChar, mCurrencyId, + beingComponent->getAttributeBase(mCurrency); + beingComponent->setAttribute(*mChar, mCurrency, currentMoney + amount * i->cost); } else { const double currentMoney = - beingComponent->getAttributeBase(mCurrencyId); + beingComponent->getAttributeBase(mCurrency); amount = std::min(amount, ((int)currentMoney) / i->cost); amount -= inv.insert(id, amount); - beingComponent->setAttribute(*mChar, mCurrencyId, + beingComponent->setAttribute(*mChar, mCurrency, currentMoney - amount * i->cost); } if (i->amount) diff --git a/src/game-server/buysell.h b/src/game-server/buysell.h index 0906aaf4..524bec9b 100644 --- a/src/game-server/buysell.h +++ b/src/game-server/buysell.h @@ -23,6 +23,8 @@ #include <vector> +#include "game-server/attributemanager.h" + class Entity; class BuySell @@ -77,7 +79,7 @@ class BuySell typedef std::vector< TradedItem > TradedItems; /** The attribute ID of the currency to use. Hardcoded for now (FIXME) */ - unsigned mCurrencyId; + AttributeManager::AttributeInfo *mCurrency; Entity *mChar; /**< Character involved. */ TradedItems mItems; /**< Traded items. */ diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp index a08b07ff..7e49f0ab 100644 --- a/src/game-server/character.cpp +++ b/src/game-server/character.cpp @@ -83,8 +83,8 @@ CharacterComponent::CharacterComponent(Entity &entity, MessageIn &msg): auto &attributeScope = attributeManager->getAttributeScope(CharacterScope); LOG_DEBUG("Character creation: initialisation of " << attributeScope.size() << " attributes."); - for (auto &attributeIt : attributeScope) - beingComponent->createAttribute(attributeIt.first, attributeIt.second); + for (auto &attribute : attributeScope) + beingComponent->createAttribute(attribute); auto *actorComponent = entity.getComponent<ActorComponent>(); actorComponent->setWalkMask(Map::BLOCKMASK_WALL); @@ -139,7 +139,8 @@ void CharacterComponent::deserialize(Entity &entity, MessageIn &msg) { unsigned id = msg.readInt16(); double base = msg.readDouble(); - beingComponent->setAttribute(entity, id, base); + auto *attributeInfo = attributeManager->getAttributeInfo(id); + beingComponent->setAttribute(entity, attributeInfo, base); } // status effects currently affecting the character @@ -224,7 +225,7 @@ void CharacterComponent::serialize(Entity &entity, MessageOut &msg) msg.writeInt16(attributes.size()); for (auto attributeIt : attributes) { - msg.writeInt16(attributeIt.first); + msg.writeInt16(attributeIt.first->id); msg.writeDouble(attributeIt.second.getBase()); msg.writeDouble(attributeIt.second.getModifiedAttribute()); } @@ -321,8 +322,11 @@ void CharacterComponent::respawn(Entity &entity) return; // No script respawn callback set - fall back to hardcoded logic - const double maxHp = beingComponent->getModifiedAttribute(ATTR_MAX_HP); - beingComponent->setAttribute(entity, ATTR_HP, maxHp); + 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); @@ -442,13 +446,11 @@ 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) + for (AttributeManager::AttributeInfo *attribute : mModifiedAttributes) { - int attr = *i; - attribMsg.writeInt16(attr); - attribMsg.writeInt32(beingComponent->getAttributeBase(attr) * 256); - attribMsg.writeInt32(beingComponent->getModifiedAttribute(attr) * 256); + 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(); @@ -466,15 +468,16 @@ void CharacterComponent::modifiedAllAttributes(Entity &entity) } } -void CharacterComponent::attributeChanged(Entity *entity, unsigned attr) +void CharacterComponent::attributeChanged(Entity *entity, + AttributeManager::AttributeInfo *attribute) { 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); + accountHandler->updateAttributes(getDatabaseID(), attribute->id, + beingComponent->getAttributeBase(attribute), + beingComponent->getModifiedAttribute(attribute)); + mModifiedAttributes.insert(attribute); } void CharacterComponent::incrementKillCount(int monsterType) @@ -501,11 +504,11 @@ int CharacterComponent::getKillCount(int monsterType) const } AttribmodResponseCode CharacterComponent::useCharacterPoint(Entity &entity, - int attribute) + AttributeManager::AttributeInfo *attribute) { auto *beingComponent = entity.getComponent<BeingComponent>(); - if (!attributeManager->isAttributeDirectlyModifiable(attribute)) + if (!attribute->modifiable) return ATTRIBMOD_INVALID_ATTRIBUTE; if (!mAttributePoints) return ATTRIBMOD_NO_POINTS_LEFT; @@ -519,11 +522,11 @@ AttribmodResponseCode CharacterComponent::useCharacterPoint(Entity &entity, } AttribmodResponseCode CharacterComponent::useCorrectionPoint(Entity &entity, - int attribute) + AttributeManager::AttributeInfo *attribute) { auto *beingComponent = entity.getComponent<BeingComponent>(); - if (!attributeManager->isAttributeDirectlyModifiable(attribute)) + if (!attribute->modifiable) return ATTRIBMOD_INVALID_ATTRIBUTE; if (!mCorrectionPoints) return ATTRIBMOD_NO_POINTS_LEFT; diff --git a/src/game-server/character.h b/src/game-server/character.h index 799b0817..a4f881d8 100644 --- a/src/game-server/character.h +++ b/src/game-server/character.h @@ -178,7 +178,7 @@ class CharacterComponent : public Component * @param being th being of which the attribute was changed * @param attributeId the changed id */ - void attributeChanged(Entity *being, unsigned attributeId); + void attributeChanged(Entity *being, AttributeManager::AttributeInfo *); /** * Calls all the "disconnected" listener. @@ -221,14 +221,14 @@ class CharacterComponent : public Component * basic attribute */ AttribmodResponseCode useCharacterPoint(Entity &entity, - int attribute); + AttributeManager::AttributeInfo *); /** * Tries to use a correction point to reduce a * basic attribute and regain a character point */ AttribmodResponseCode useCorrectionPoint(Entity &entity, - int attribute); + AttributeManager::AttributeInfo *); void setAttributePoints(int points); int getAttributePoints() const; @@ -320,7 +320,7 @@ class CharacterComponent : public Component Possessions mPossessions; /**< Possesssions of the character. */ /** Attributes modified since last update. */ - std::set<size_t> mModifiedAttributes; + std::set<AttributeManager::AttributeInfo *> mModifiedAttributes; std::set<unsigned> mModifiedAbilities; diff --git a/src/game-server/commandhandler.cpp b/src/game-server/commandhandler.cpp index 4c2adad3..ca08a636 100644 --- a/src/game-server/commandhandler.cpp +++ b/src/game-server/commandhandler.cpp @@ -671,9 +671,11 @@ static void handleMoney(Entity *player, std::string &args) auto *beingComponent = other->getComponent<BeingComponent>(); + auto *moneyAttribute = attributeManager->getAttributeInfo(ATTR_GP); + // change how much money the player has - const double previousMoney = beingComponent->getAttributeBase(ATTR_GP); - beingComponent->setAttribute(*player, ATTR_GP , previousMoney + value); + const double previousMoney = beingComponent->getAttributeBase(moneyAttribute); + beingComponent->setAttribute(*player, moneyAttribute , previousMoney + value); // log transaction std::string msg = "User created " + valuestr + " money"; @@ -1065,7 +1067,6 @@ static void handleTakePermission(Entity *player, std::string &args) static void handleAttribute(Entity *player, std::string &args) { Entity *other; - int attr, value; // get arguments std::string character = getArgument(args); @@ -1104,16 +1105,16 @@ static void handleAttribute(Entity *player, std::string &args) } // put the attribute into an integer - attr = utils::stringToInt(attrstr); + int attributeId = utils::stringToInt(attrstr); - if (attr < 0) + if (attributeId < 0) { say("Invalid Attribute", player); return; } // put the value into an integer - value = utils::stringToInt(valuestr); + int value = utils::stringToInt(valuestr); if (value < 0) { @@ -1123,12 +1124,14 @@ static void handleAttribute(Entity *player, std::string &args) auto *beingComponent = other->getComponent<BeingComponent>(); + auto *attribute = attributeManager->getAttributeInfo(attributeId); + // change the player's attribute - beingComponent->setAttribute(*other, attr, value); + beingComponent->setAttribute(*other, attribute, value); // log transaction std::stringstream msg; - msg << "User changed attribute " << attr << " of player " + msg << "User changed attribute " << attribute->id << " of player " << beingComponent->getName() << " to " << value; int databaseId = @@ -1264,7 +1267,8 @@ static void handleMute(Entity *player, std::string &args) static void handleDie(Entity *player, std::string &) { - player->getComponent<BeingComponent>()->setAttribute(*player, ATTR_HP, 0); + auto *hpAttribute = attributeManager->getAttributeInfo(ATTR_HP); + player->getComponent<BeingComponent>()->setAttribute(*player, hpAttribute, 0); say("You've killed yourself.", player); } @@ -1284,7 +1288,8 @@ static void handleKill(Entity *player, std::string &args) } // kill the player - other->getComponent<BeingComponent>()->setAttribute(*player, ATTR_HP, 0); + auto *hpAttribute = attributeManager->getAttributeInfo(ATTR_HP); + other->getComponent<BeingComponent>()->setAttribute(*player, hpAttribute, 0); // feedback std::stringstream targetMsg; diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp index c7ff655a..e1f58967 100644 --- a/src/game-server/gamehandler.cpp +++ b/src/game-server/gamehandler.cpp @@ -876,14 +876,15 @@ void GameHandler::handleRaiseAttribute(GameClient &client, MessageIn &message) auto *characterComponent = client.character->getComponent<CharacterComponent>(); - const int attribute = message.readInt16(); + const int attributeId = message.readInt16(); + auto *attribute = attributeManager->getAttributeInfo(attributeId); AttribmodResponseCode retCode; retCode = characterComponent->useCharacterPoint(*client.character, attribute); MessageOut result(GPMSG_RAISE_ATTRIBUTE_RESPONSE); result.writeInt8(retCode); - result.writeInt16(attribute); + result.writeInt16(attributeId); client.send(result); if (retCode == ATTRIBMOD_OK) @@ -906,14 +907,15 @@ void GameHandler::handleLowerAttribute(GameClient &client, MessageIn &message) auto *characterComponent = client.character->getComponent<CharacterComponent>(); - const int attribute = message.readInt32(); + const int attributeId = message.readInt32(); + auto *attribute = attributeManager->getAttributeInfo(attributeId); AttribmodResponseCode retCode; retCode = characterComponent->useCorrectionPoint(*client.character, attribute); MessageOut result(GPMSG_LOWER_ATTRIBUTE_RESPONSE); result.writeInt8(retCode); - result.writeInt16(attribute); + result.writeInt16(attributeId); client.send(result); if (retCode == ATTRIBMOD_OK) diff --git a/src/game-server/item.cpp b/src/game-server/item.cpp index 589fc011..5e5ca3d9 100644 --- a/src/game-server/item.cpp +++ b/src/game-server/item.cpp @@ -35,9 +35,11 @@ bool ItemEffectAttrMod::apply(Entity *itemUser) { LOG_DEBUG("Applying modifier."); itemUser->getComponent<BeingComponent>()->applyModifier(*itemUser, - mAttributeId, mMod, + mAttribute, + mMod, mAttributeLayer, - mDuration, mId); + mDuration, + mModId); return false; } @@ -45,10 +47,11 @@ void ItemEffectAttrMod::dispell(Entity *itemUser) { LOG_DEBUG("Dispelling modifier."); itemUser->getComponent<BeingComponent>()->removeModifier(*itemUser, - mAttributeId, + mAttribute, mMod, mAttributeLayer, - mId, !mDuration); + mModId, + !mDuration); } ItemEffectScript::~ItemEffectScript() diff --git a/src/game-server/item.h b/src/game-server/item.h index e627b42b..8f7b114a 100644 --- a/src/game-server/item.h +++ b/src/game-server/item.h @@ -110,21 +110,25 @@ class ItemEffectInfo class ItemEffectAttrMod : public ItemEffectInfo { public: - ItemEffectAttrMod(unsigned attrId, unsigned layer, double value, - unsigned id, unsigned duration = 0) : - mAttributeId(attrId), mAttributeLayer(layer), - mMod(value), mDuration(duration), mId(id) + ItemEffectAttrMod(AttributeManager::AttributeInfo *attribute, + unsigned layer, double value, unsigned modId, + unsigned duration = 0) + : mAttribute(attribute) + , mAttributeLayer(layer) + , mMod(value) + , mDuration(duration) + , mModId(modId) {} bool apply(Entity *itemUser); void dispell(Entity *itemUser); private: - unsigned mAttributeId; + AttributeManager::AttributeInfo *mAttribute; unsigned mAttributeLayer; double mMod; unsigned mDuration; - unsigned mId; + unsigned mModId; }; class ItemEffectConsumes : public ItemEffectInfo diff --git a/src/game-server/itemmanager.cpp b/src/game-server/itemmanager.cpp index bcb64072..1ca61a65 100644 --- a/src/game-server/itemmanager.cpp +++ b/src/game-server/itemmanager.cpp @@ -338,7 +338,9 @@ void ItemManager::readEffectNode(xmlNodePtr effectNode, ItemClass *item) 0); ModifierLocation location = attributeManager->getLocation(tag); double value = XML::getFloatProperty(subNode, "value", 0.0); - item->addEffect(new ItemEffectAttrMod(location.attributeId, + + auto *attribute = attributeManager->getAttributeInfo(location.attributeId); + item->addEffect(new ItemEffectAttrMod(attribute, location.layer, value, item->getDatabaseID(), diff --git a/src/game-server/monster.cpp b/src/game-server/monster.cpp index 41a69ecb..cf589977 100644 --- a/src/game-server/monster.cpp +++ b/src/game-server/monster.cpp @@ -51,9 +51,9 @@ MonsterComponent::MonsterComponent(Entity &entity, MonsterClass *specy): */ auto *beingComponent = entity.getComponent<BeingComponent>(); - for (auto attrInfo : attributeManager->getAttributeScope(MonsterScope)) + for (auto attribute : attributeManager->getAttributeScope(MonsterScope)) { - beingComponent->createAttribute(attrInfo.first, attrInfo.second); + beingComponent->createAttribute(attribute); } /* @@ -63,15 +63,15 @@ MonsterComponent::MonsterComponent(Entity &entity, MonsterClass *specy): int mutation = specy->getMutation(); - for (auto &attribute : specy->getAttributes()) + for (auto &attributeIt : specy->getAttributes()) { - double attributeValue = attribute.second; + double attributeValue = attributeIt.second; if (mutation != 0) { double factor = 100 + (rand() % (mutation * 2)) - mutation; attributeValue = attributeValue * factor / 100.0; } - beingComponent->setAttribute(entity, attribute.first->id, + beingComponent->setAttribute(entity, attributeIt.first, attributeValue); } diff --git a/src/game-server/monster.h b/src/game-server/monster.h index e50f7451..ff534f58 100644 --- a/src/game-server/monster.h +++ b/src/game-server/monster.h @@ -105,10 +105,9 @@ class MonsterClass /** * Sets a being base attribute. */ - void setAttribute(const AttributeManager::AttributeInfo *attribute, - double value); + void setAttribute(AttributeManager::AttributeInfo *attribute, double value); - const std::map<const AttributeManager::AttributeInfo *, double> + const std::map<AttributeManager::AttributeInfo *, double> &getAttributes() const; /** Sets collision circle radius. */ @@ -138,7 +137,7 @@ class MonsterClass BeingGender mGender; MonsterDrops mDrops; - std::map<const AttributeManager::AttributeInfo *, double> mAttributes; + std::map<AttributeManager::AttributeInfo *, double> mAttributes; std::set<AbilityManager::AbilityInfo *> mAbilities; float mSpeed; /**< The monster class speed in tiles per second */ int mSize; @@ -190,13 +189,12 @@ class MonsterComponent : public Component Timeout mDecayTimeout; }; -inline void MonsterClass::setAttribute( - const AttributeManager::AttributeInfo *attribute, double value) +inline void MonsterClass::setAttribute(AttributeManager::AttributeInfo *attribute, double value) { mAttributes[attribute] = value; } -inline const std::map<const AttributeManager::AttributeInfo *, double> +inline const std::map<AttributeManager::AttributeInfo *, double> &MonsterClass::getAttributes() const { return mAttributes; diff --git a/src/game-server/monstermanager.cpp b/src/game-server/monstermanager.cpp index 327c5c10..1802a022 100644 --- a/src/game-server/monstermanager.cpp +++ b/src/game-server/monstermanager.cpp @@ -166,7 +166,7 @@ void MonsterManager::readMonsterNode(xmlNodePtr node, const std::string &filenam { std::string attributeIdString = XML::getProperty(subnode, "id", std::string()); - const AttributeManager::AttributeInfo *info; + AttributeManager::AttributeInfo *info = nullptr; if (utils::isNumeric(attributeIdString)) { const int attributeId = utils::stringToInt(attributeIdString); diff --git a/src/game-server/spawnareacomponent.cpp b/src/game-server/spawnareacomponent.cpp index 33bd5acd..da715f4f 100644 --- a/src/game-server/spawnareacomponent.cpp +++ b/src/game-server/spawnareacomponent.cpp @@ -71,7 +71,8 @@ void SpawnAreaComponent::update(Entity &entity) being->addComponent(beingComponent); being->addComponent(new MonsterComponent(*being, mSpecy)); - if (beingComponent->getModifiedAttribute(ATTR_MAX_HP) <= 0) + auto *hpAttribute = attributeManager->getAttributeInfo(ATTR_MAX_HP); + if (beingComponent->getModifiedAttribute(hpAttribute) <= 0) { LOG_WARN("Refusing to spawn dead monster " << mSpecy->getId()); delete being; diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp index d2aaff4e..9fab3431 100644 --- a/src/game-server/state.cpp +++ b/src/game-server/state.cpp @@ -342,9 +342,10 @@ static void informPlayer(MapComposite *map, Entity *p) // We multiply the sent speed (in tiles per second) by ten // to get it within a byte with decimal precision. // For instance, a value of 4.5 will be sent as 45. + auto *tpsSpeedAttribute = attributeManager->getAttributeInfo(ATTR_MOVE_SPEED_TPS); moveMsg.writeInt8((unsigned short) (o->getComponent<BeingComponent>() - ->getModifiedAttribute(ATTR_MOVE_SPEED_TPS) * 10)); + ->getModifiedAttribute(tpsSpeedAttribute) * 10)); } } @@ -379,10 +380,12 @@ static void informPlayer(MapComposite *map, Entity *p) MessageOut healthMsg(GPMSG_BEING_HEALTH_CHANGE); healthMsg.writeInt16( c->getComponent<ActorComponent>()->getPublicID()); + auto *hpAttribute = attributeManager->getAttributeInfo(ATTR_HP); healthMsg.writeInt16( - beingComponent->getModifiedAttribute(ATTR_HP)); + beingComponent->getModifiedAttribute(hpAttribute)); + auto *maxHpAttribute = attributeManager->getAttributeInfo(ATTR_MAX_HP); healthMsg.writeInt16( - beingComponent->getModifiedAttribute(ATTR_MAX_HP)); + beingComponent->getModifiedAttribute(maxHpAttribute)); gameHandler->sendTo(p, healthMsg); } } diff --git a/src/game-server/trade.cpp b/src/game-server/trade.cpp index 3fbcd0e6..b30eaf79 100644 --- a/src/game-server/trade.cpp +++ b/src/game-server/trade.cpp @@ -37,8 +37,13 @@ * TRADE_AGREE_WAIT : One player has agreed, waiting for the other one */ -Trade::Trade(Entity *c1, Entity *c2): - mChar1(c1), mChar2(c2), mMoney1(0), mMoney2(0), mState(TRADE_INIT), mCurrencyId(ATTR_GP) +Trade::Trade(Entity *c1, Entity *c2) + : mChar1(c1) + , mChar2(c2) + , mMoney1(0) + , mMoney2(0) + , mState(TRADE_INIT) + , mCurrencyAttribute(attributeManager->getAttributeInfo(ATTR_GP)) { MessageOut msg(GPMSG_TRADE_REQUEST); msg.writeInt16(c1->getComponent<ActorComponent>()->getPublicID()); @@ -134,9 +139,9 @@ void Trade::agree(Entity *c) Inventory v1(mChar1), v2(mChar2); const double moneyChar1 = mChar1->getComponent<BeingComponent>() - ->getAttributeBase(mCurrencyId); + ->getAttributeBase(mCurrencyAttribute); const double moneyChar2 = mChar2->getComponent<BeingComponent>() - ->getAttributeBase(mCurrencyId); + ->getAttributeBase(mCurrencyAttribute); if (moneyChar1 >= mMoney1 - mMoney2 && moneyChar2 >= mMoney2 - mMoney1 && @@ -144,10 +149,10 @@ void Trade::agree(Entity *c) perform(mItems2, v2, v1)) { mChar1->getComponent<BeingComponent>() - ->setAttribute(*mChar1, mCurrencyId, + ->setAttribute(*mChar1, mCurrencyAttribute, moneyChar1 - mMoney1 + mMoney2); mChar2->getComponent<BeingComponent>() - ->setAttribute(*mChar2, mCurrencyId, + ->setAttribute(*mChar2, mCurrencyAttribute, moneyChar2 - mMoney2 + mMoney1); } else diff --git a/src/game-server/trade.h b/src/game-server/trade.h index 72ec0b3d..f9009113 100644 --- a/src/game-server/trade.h +++ b/src/game-server/trade.h @@ -23,6 +23,8 @@ #include <vector> +#include "game-server/attributemanager.h" + class Entity; class Inventory; @@ -102,7 +104,7 @@ class Trade TradedItems mItems1, mItems2; /**< Traded items. */ int mMoney1, mMoney2; /**< Traded money. */ TradeState mState; /**< State of transaction. */ - unsigned mCurrencyId; /**< The attribute to use as currency. */ + AttributeManager::AttributeInfo *mCurrencyAttribute; }; #endif diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index c151b9fa..0193d888 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -289,8 +289,10 @@ static int npc_create(lua_State *s) 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); + auto *maxHpAttribute = attributeManager->getAttributeInfo(ATTR_MAX_HP); + beingComponent->setAttribute(*npc, maxHpAttribute, 100); + auto *hpAttribute = attributeManager->getAttributeInfo(ATTR_HP); + beingComponent->setAttribute(*npc, hpAttribute, 100); beingComponent->setName(name); beingComponent->setGender(getGender(gender)); @@ -1367,11 +1369,8 @@ static int entity_walk(lua_State *s) if (lua_gettop(s) >= 4) { 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)); + auto *tpsSpeedAttribute = attributeManager->getAttributeInfo(ATTR_MOVE_SPEED_TPS); + beingComponent->setAttribute(*being, tpsSpeedAttribute, speedTps); } return 0; @@ -1724,10 +1723,9 @@ static int entity_get_y(lua_State *s) static int entity_get_base_attribute(lua_State *s) { Entity *being = checkBeing(s, 1); - int attr = checkAttribute(s, 2)->id; - luaL_argcheck(s, attr > 0, 2, "invalid attribute id"); + auto *attribute = checkAttribute(s, 2); - lua_pushinteger(s, being->getComponent<BeingComponent>()->getAttributeBase(attr)); + lua_pushinteger(s, being->getComponent<BeingComponent>()->getAttributeBase(attribute)); return 1; } @@ -1742,10 +1740,10 @@ static int entity_get_base_attribute(lua_State *s) static int entity_set_base_attribute(lua_State *s) { Entity *being = checkBeing(s, 1); - int attr = checkAttribute(s, 2)->id; + auto *attribute = checkAttribute(s, 2); double value = luaL_checknumber(s, 3); - being->getComponent<BeingComponent>()->setAttribute(*being, attr, value); + being->getComponent<BeingComponent>()->setAttribute(*being, attribute, value); return 0; } @@ -1772,11 +1770,10 @@ static int entity_set_base_attribute(lua_State *s) static int entity_get_modified_attribute(lua_State *s) { Entity *being = checkBeing(s, 1); - int attr = checkAttribute(s, 2)->id; - luaL_argcheck(s, attr > 0, 2, "invalid attribute id"); + auto *attribute = checkAttribute(s, 2); const double value = - being->getComponent<BeingComponent>()->getModifiedAttribute(attr); + being->getComponent<BeingComponent>()->getModifiedAttribute(attribute); lua_pushinteger(s, value); return 1; } @@ -1802,15 +1799,15 @@ static int entity_get_modified_attribute(lua_State *s) static int entity_apply_attribute_modifier(lua_State *s) { Entity *being = checkBeing(s, 1); - int attr = checkAttribute(s, 2)->id; + auto *attribute = checkAttribute(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->getComponent<BeingComponent>()->applyModifier(*being, attr, value, - layer, duration, - effectId); + being->getComponent<BeingComponent>()->applyModifier(*being, attribute, + value, layer, + duration, effectId); return 0; } @@ -1825,13 +1822,14 @@ static int entity_apply_attribute_modifier(lua_State *s) static int entity_remove_attribute_modifier(lua_State *s) { Entity *being = checkBeing(s, 1); - int attr = checkAttribute(s, 2)->id; + auto *attribute = checkAttribute(s, 2); double value = luaL_checknumber(s, 3); int layer = luaL_checkint(s, 4); int effectId = luaL_optint(s, 5, 0); - being->getComponent<BeingComponent>()->removeModifier(*being, attr, value, - layer, effectId); + being->getComponent<BeingComponent>()->removeModifier(*being, attribute, + value, layer, + effectId); return 0; } @@ -2910,6 +2908,47 @@ static int abilityinfo_on_recharged(lua_State *s) } +/** LUA_CATEGORY AttributeInfo class (attributeinfoclass) + */ + +/** LUA get_attribute_info (attributeinfoclass) + * local attributeinfo = get_attribute_info(string name) + * local attributeinfo = get_attribute_info(int id) + ** + * **Return value:** The attribute info of the passed attribute. + */ +static int get_attribute_info(lua_State *s) +{ + auto *attributeInfo = checkAttribute(s, 1); + LuaAttributeInfo::push(s, attributeInfo); + return 1; +} + +/** LUA attributeinfo:name (attributeinfoclass) + * local id = attributeinfo:id() + ** + * **Return value:** The id of the `attributeinfo`. + */ +static int attributeinfo_get_id(lua_State *s) +{ + auto *attributeInfo = LuaAttributeInfo::check(s, 1); + lua_pushinteger(s, attributeInfo->id); + return 1; +} + +/** LUA attributeinfo:name (attributeinfoclass) + * local name = attributeinfo:name() + ** + * **Return value:** The name of the `attributeinfo`. + */ +static int attributeinfo_get_name(lua_State *s) +{ + auto *attributeInfo = LuaAttributeInfo::check(s, 1); + lua_pushstring(s, attributeInfo->name.c_str()); + return 1; +} + + /** LUA_CATEGORY Status effect class (statuseffectclass) */ @@ -3317,6 +3356,7 @@ LuaScript::LuaScript(): { "map_get_objects", map_get_objects }, { "announce", announce }, { "get_ability_info", get_ability_info }, + { "get_attribute_info", get_attribute_info }, { nullptr, nullptr } }; #if LUA_VERSION_NUM < 502 @@ -3428,12 +3468,19 @@ LuaScript::LuaScript(): { nullptr, nullptr} }; + static luaL_Reg const members_AttributeInfo[] = { + { "id", attributeinfo_get_id }, + { "name", attributeinfo_get_name }, + { nullptr, nullptr} + }; + LuaEntity::registerType(mRootState, "Entity", members_Entity); LuaItemClass::registerType(mRootState, "ItemClass", members_ItemClass); LuaMapObject::registerType(mRootState, "MapObject", members_MapObject); LuaMonsterClass::registerType(mRootState, "MonsterClass", members_MonsterClass); LuaStatusEffect::registerType(mRootState, "StatusEffect", members_StatusEffect); LuaAbilityInfo::registerType(mRootState, "AbilityInfo", members_AbilityInfo); + LuaAttributeInfo::registerType(mRootState, "AttributeInfo", members_AttributeInfo); // Make script object available to callback functions. lua_pushlightuserdata(mRootState, const_cast<char *>(®istryKey)); diff --git a/src/scripting/luascript.cpp b/src/scripting/luascript.cpp index 1617f74c..5ce55d13 100644 --- a/src/scripting/luascript.cpp +++ b/src/scripting/luascript.cpp @@ -113,6 +113,13 @@ void LuaScript::push(const std::list<InventoryItem> &itemList) ++nbArgs; } +void LuaScript::push(AttributeManager::AttributeInfo *attributeInfo) +{ + assert(nbArgs >= 0); + ::push(mCurrentState, attributeInfo); + ++nbArgs; +} + int LuaScript::execute(const Context &context) { assert(nbArgs >= 0); diff --git a/src/scripting/luascript.h b/src/scripting/luascript.h index b874976a..a30cbf08 100644 --- a/src/scripting/luascript.h +++ b/src/scripting/luascript.h @@ -54,12 +54,10 @@ class LuaScript : public Script void prepareResume(Thread *thread); void push(int); - void push(const std::string &); - void push(Entity *); - void push(const std::list<InventoryItem> &itemList); + void push(AttributeManager::AttributeInfo *); int execute(const Context &context = Context()); diff --git a/src/scripting/luautil.cpp b/src/scripting/luautil.cpp index c9738694..5a0cf883 100644 --- a/src/scripting/luautil.cpp +++ b/src/scripting/luautil.cpp @@ -226,13 +226,22 @@ AbilityManager::AbilityInfo *checkAbility(lua_State *s, int p) return abilityInfo; } -const AttributeManager::AttributeInfo *checkAttribute(lua_State *s, int p) +AttributeManager::AttributeInfo *checkAttribute(lua_State *s, int p) { - const AttributeManager::AttributeInfo *attributeInfo; - if (lua_isnumber(s, p)) + AttributeManager::AttributeInfo *attributeInfo; + + switch (lua_type(s, p)) + { + case LUA_TNUMBER: attributeInfo = attributeManager->getAttributeInfo(luaL_checkint(s, p)); - else + break; + case LUA_TSTRING: attributeInfo = attributeManager->getAttributeInfo(luaL_checkstring(s, p)); + break; + case LUA_TUSERDATA: + attributeInfo = LuaAttributeInfo::check(s, p); + break; + } luaL_argcheck(s, attributeInfo != nullptr, p, "invalid attribute"); return attributeInfo; diff --git a/src/scripting/luautil.h b/src/scripting/luautil.h index 220d4c2f..03c4666e 100644 --- a/src/scripting/luautil.h +++ b/src/scripting/luautil.h @@ -165,6 +165,7 @@ typedef LuaUserData<MapObject> LuaMapObject; typedef LuaUserData<MonsterClass> LuaMonsterClass; typedef LuaUserData<StatusEffect> LuaStatusEffect; typedef LuaUserData<AbilityManager::AbilityInfo> LuaAbilityInfo; +typedef LuaUserData<AttributeManager::AttributeInfo> LuaAttributeInfo; Script * getScript(lua_State *s); @@ -179,7 +180,7 @@ Entity * checkMonster(lua_State *s, int p); MonsterClass * checkMonsterClass(lua_State *s, int p); Entity * checkNpc(lua_State *s, int p); AbilityManager::AbilityInfo * checkAbility(lua_State *s, int p); -const AttributeManager::AttributeInfo *checkAttribute(lua_State *s, int p); +AttributeManager::AttributeInfo * checkAttribute(lua_State *s, int p); unsigned char checkWalkMask(lua_State *s, int p); MapComposite * checkCurrentMap(lua_State *s, Script *script = 0); @@ -219,6 +220,11 @@ inline void push(lua_State *s, MonsterClass *val) LuaMonsterClass::push(s, val); } +inline void push(lua_State *s, AttributeManager::AttributeInfo *val) +{ + LuaAttributeInfo::push(s, val); +} + /* Pushes an STL LIST */ template <typename T> diff --git a/src/scripting/script.h b/src/scripting/script.h index 7cf457cc..1f8c415e 100644 --- a/src/scripting/script.h +++ b/src/scripting/script.h @@ -24,6 +24,8 @@ #include "common/inventorydata.h" #include "common/manaserv_protocol.h" +#include "game-server/attributemanager.h" + #include <list> #include <string> #include <vector> @@ -193,6 +195,8 @@ class Script : public sigc::trackable */ virtual void push(const std::list<InventoryItem> &itemList) = 0; + virtual void push(AttributeManager::AttributeInfo *) = 0; + /** * Executes the function being prepared. * @param context the context that is supposed to be used for executing |