/* * 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 "attribute.h" #include "game-server/being.h" #include "utils/logger.h" #include AttributeModifiersEffect::AttributeModifiersEffect(StackableType stackableType, ModifierEffectType effectType) : mCacheVal(0), mMod(effectType == Multiplicative ? 1 : 0), mStackableType(stackableType), mEffectType(effectType) { assert(effectType == Multiplicative || effectType == Additive); assert(stackableType == Stackable || stackableType == NonStackable || stackableType == NonStackableBonus); LOG_DEBUG("Layer created with effectType " << effectType << " and stackableType " << stackableType << "."); } AttributeModifiersEffect::~AttributeModifiersEffect() { // ? /*mStates.clear();*/ LOG_WARN("DELETION of attribute effect!"); } bool AttributeModifiersEffect::add(unsigned short duration, double value, double prevLayerValue, int level) { LOG_DEBUG("Adding modifier with value " << value << " with a previous layer value of " << prevLayerValue << ". " "Current mod at this layer: " << mMod << "."); bool ret = false; mStates.push_back(new AttributeModifierState(duration, value, level)); switch (mStackableType) { case Stackable: switch (mEffectType) { case Additive: if (value) { ret = true; mMod += value; mCacheVal = prevLayerValue + mMod; } break; case Multiplicative: if (value != 1) { ret = true; mMod *= value; mCacheVal = prevLayerValue * mMod; } break; default: LOG_FATAL("Attribute modifiers effect: unhandled type '" << mEffectType << "' as a stackable!"); assert(0); break; } break; case NonStackable: switch (mEffectType) { case Additive: if (value > mMod) { ret = true; mMod = value; if (mMod > prevLayerValue) mCacheVal = mMod; } break; default: LOG_FATAL("Attribute modifiers effect: unhandled type '" << mEffectType << "' as a non-stackable!"); assert(0); } // A multiplicative type would also be nonsensical break; case NonStackableBonus: switch (mEffectType) { case Additive: case Multiplicative: if (value > mMod) { ret = true; mMod = value; mCacheVal = mEffectType == Additive ? prevLayerValue + mMod : prevLayerValue * mMod; } break; default: LOG_FATAL("Attribute modifiers effect: unhandled type '" << mEffectType << "' as a non-stackable bonus!"); assert(0); } break; default: LOG_FATAL("Attribute modifiers effect: unknown stackable type '" << mStackableType << "'!"); assert(0); } return ret; } bool durationCompare(const AttributeModifierState *lhs, const AttributeModifierState *rhs) { return lhs->mDuration < rhs->mDuration; } bool AttributeModifiersEffect::remove(double value, unsigned id, bool fullCheck) { /* We need to find and check this entry exists, and erase the entry from the list too. */ if (!fullCheck) mStates.sort(durationCompare); /* Search only through those with a duration of 0. */ bool ret = false; for (std::list< AttributeModifierState * >::iterator it = mStates.begin(); it != mStates.end() && (fullCheck || !(*it)->mDuration);) { /* Check for a match */ if ((*it)->mValue != value || (*it)->mId != id) { ++it; continue; } delete *it; mStates.erase(it++); /* If this is stackable, we need to update for every modifier affected */ if (mStackableType == Stackable) updateMod(value); ret = true; if (!id) break; } /* * Non stackables only need to be updated once, since this is recomputed * from scratch. This is done at the end after modifications have been * made as necessary. */ if (ret && mStackableType != Stackable) updateMod(); return ret; } void AttributeModifiersEffect::updateMod(double value) { if (mStackableType == Stackable) { if (mEffectType == Additive) { mMod -= value; } else if (mEffectType == Multiplicative) { if (value) mMod /= value; else { mMod = 1; for (std::list< AttributeModifierState * >::const_iterator it = mStates.begin(), it_end = mStates.end(); it != it_end; ++it) mMod *= (*it)->mValue; } } else LOG_ERROR("Attribute modifiers effect: unhandled type '" << mEffectType << "' as a stackable in cache update!"); } else if (mStackableType == NonStackable || mStackableType == NonStackableBonus) { if (mMod == value) { mMod = 0; for (std::list< AttributeModifierState * >::const_iterator it = mStates.begin(), it_end = mStates.end(); it != it_end; ++it) if ((*it)->mValue > mMod) mMod = (*it)->mValue; } } else { LOG_ERROR("Attribute modifiers effect: unknown stackable type '" << mStackableType << "' in cache update!"); } } bool AttributeModifiersEffect::recalculateModifiedValue(double newPrevLayerValue) { double oldValue = mCacheVal; switch (mEffectType) { case Additive: switch (mStackableType) { case Stackable: case NonStackableBonus: mCacheVal = newPrevLayerValue + mMod; break; case NonStackable: mCacheVal = newPrevLayerValue < mMod ? mMod : newPrevLayerValue; break; default: LOG_FATAL("Unknown effect type '" << mEffectType << "'!"); assert(0); } break; case Multiplicative: mCacheVal = mStackableType == Stackable ? newPrevLayerValue * mMod : newPrevLayerValue * mMod; break; default: LOG_FATAL("Unknown effect type '" << mEffectType << "'!"); assert(0); } return oldValue != mCacheVal; } bool Attribute::add(unsigned short duration, double value, unsigned layer, int id) { assert(mMods.size() > layer); LOG_DEBUG("Adding modifier to attribute with duration " << duration << ", value " << value << ", at layer " << layer << " with id " << id); if (mMods.at(layer)->add(duration, value, (layer ? mMods.at(layer - 1)->getCachedModifiedValue() : mBase) , id)) { while (++layer < mMods.size()) { if (!mMods.at(layer)->recalculateModifiedValue( mMods.at(layer - 1)->getCachedModifiedValue())) { LOG_DEBUG("Modifier added, but modified value not changed."); return false; } } LOG_DEBUG("Modifier added. Base value: " << mBase << ", new modified " "value: " << getModifiedAttribute() << "."); return true; } LOG_DEBUG("Failed to add modifier!"); return false; } bool Attribute::remove(double value, unsigned layer, int lvl, bool fullcheck) { assert(mMods.size() > layer); if (mMods.at(layer)->remove(value, lvl, fullcheck)) { for (; layer < mMods.size(); ++layer) if (!mMods.at(layer)->recalculateModifiedValue( layer ? mMods.at(layer - 1)->getCachedModifiedValue() : mBase)) return false; return true; } return false; } bool AttributeModifiersEffect::tick() { bool ret = false; std::list::iterator it = mStates.begin(); while (it != mStates.end()) { if ((*it)->tick()) { double value = (*it)->mValue; LOG_DEBUG("Modifier of value " << value << " expiring!"); delete *it; mStates.erase(it++); updateMod(value); ret = true; } ++it; } return ret; } Attribute::Attribute(const AttributeManager::AttributeInfo &info): mBase(0), mMinValue(info.minimum), mMaxValue(info.maximum) { const std::vector &modifiers = info.modifiers; LOG_DEBUG("Construction of new attribute with '" << modifiers.size() << "' layers."); for (unsigned i = 0; i < modifiers.size(); ++i) { LOG_DEBUG("Adding layer with stackable type " << modifiers[i].stackableType << " and effect type " << modifiers[i].effectType << "."); mMods.push_back(new AttributeModifiersEffect(modifiers[i].stackableType, modifiers[i].effectType)); LOG_DEBUG("Layer added."); } mBase = checkBounds(mBase); } Attribute::~Attribute() { // for (std::vector::iterator it = mMods.begin(), // it_end = mMods.end(); it != it_end; ++it) // { // ? //delete *it; // } } bool Attribute::tick() { bool ret = false; double prev = mBase; for (std::vector::iterator it = mMods.begin(), it_end = mMods.end(); it != it_end; ++it) { if ((*it)->tick()) { LOG_DEBUG("Attribute layer " << mMods.begin() - it << " has expiring modifiers."); ret = true; } if (ret) if (!(*it)->recalculateModifiedValue(prev)) ret = false; prev = (*it)->getCachedModifiedValue(); } return ret; } void Attribute::clearMods() { for (std::vector::iterator it = mMods.begin(), it_end = mMods.end(); it != it_end; ++it) (*it)->clearMods(mBase); } void Attribute::setBase(double base) { base = checkBounds(base); LOG_DEBUG("Setting base attribute from " << mBase << " to " << base << "."); double prev = mBase = base; std::vector::iterator it = mMods.begin(); while (it != mMods.end()) { if ((*it)->recalculateModifiedValue(prev)) prev = (*it++)->getCachedModifiedValue(); else break; } } void AttributeModifiersEffect::clearMods(double baseValue) { mStates.clear(); mCacheVal = baseValue; mMod = mEffectType == Additive ? 0 : 1; } double Attribute::checkBounds(double baseValue) const { LOG_DEBUG("Checking bounds for value: " << baseValue); if (baseValue > mMaxValue) baseValue = mMaxValue; else if (baseValue < mMinValue) baseValue = mMinValue; return baseValue; }