/** * Tibia GIMUD Server - a free and open-source MMORPG server emulator * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program 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 * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "otpch.h" #include "condition.h" #include "game.h" extern Game g_game; bool Condition::setParam(ConditionParam_t param, int32_t value) { switch (param) { case CONDITION_PARAM_TICKS: { ticks = value; return true; } case CONDITION_PARAM_SUBID: { subId = value; return true; } default: { return false; } } } bool Condition::unserialize(PropStream& propStream) { uint8_t attr_type; while (propStream.read(attr_type) && attr_type != CONDITIONATTR_END) { if (!unserializeProp(static_cast(attr_type), propStream)) { return false; } } return true; } bool Condition::unserializeProp(ConditionAttr_t attr, PropStream& propStream) { switch (attr) { case CONDITIONATTR_TYPE: { int32_t value; if (!propStream.read(value)) { return false; } conditionType = static_cast(value); return true; } case CONDITIONATTR_ID: { int32_t value; if (!propStream.read(value)) { return false; } id = static_cast(value); return true; } case CONDITIONATTR_TICKS: { return propStream.read(ticks); } case CONDITIONATTR_SUBID: { return propStream.read(subId); } case CONDITIONATTR_END: return true; default: return false; } } void Condition::serialize(PropWriteStream& propWriteStream) { propWriteStream.write(CONDITIONATTR_TYPE); propWriteStream.write(conditionType); propWriteStream.write(CONDITIONATTR_ID); propWriteStream.write(id); propWriteStream.write(CONDITIONATTR_TICKS); propWriteStream.write(ticks); propWriteStream.write(CONDITIONATTR_SUBID); propWriteStream.write(subId); } void Condition::setTicks(int32_t newTicks) { ticks = newTicks; endTime = ticks + OTSYS_TIME(); } bool Condition::executeCondition(Creature*, int32_t interval) { if (ticks == -1) { return true; } //Not using set ticks here since it would reset endTime ticks = std::max(0, ticks - interval); return getEndTime() >= OTSYS_TIME(); } Condition* Condition::createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param/* = 0*/, uint32_t subId/* = 0*/) { switch (type) { case CONDITION_POISON: case CONDITION_FIRE: case CONDITION_ENERGY: case CONDITION_DROWN: return new ConditionDamage(id, type, subId); case CONDITION_HASTE: case CONDITION_PARALYZE: return new ConditionSpeed(id, type, ticks, subId, param); case CONDITION_INVISIBLE: return new ConditionInvisible(id, type, ticks, subId); case CONDITION_OUTFIT: return new ConditionOutfit(id, type, ticks, subId); case CONDITION_LIGHT: return new ConditionLight(id, type, ticks, subId, param & 0xFF, (param & 0xFF00) >> 8); case CONDITION_REGENERATION: return new ConditionRegeneration(id, type, ticks, subId); case CONDITION_SOUL: return new ConditionSoul(id, type, ticks, subId); case CONDITION_ATTRIBUTES: return new ConditionAttributes(id, type, ticks, subId); case CONDITION_INFIGHT: case CONDITION_DRUNK: case CONDITION_EXHAUST: case CONDITION_MUTED: case CONDITION_CHANNELMUTEDTICKS: case CONDITION_YELLTICKS: case CONDITION_PACIFIED: case CONDITION_MANASHIELD: case CONDITION_AGGRESSIVE: return new ConditionGeneric(id, type, ticks, subId); default: return nullptr; } } Condition* Condition::createCondition(PropStream& propStream) { uint8_t attr; if (!propStream.read(attr) || attr != CONDITIONATTR_TYPE) { return nullptr; } uint32_t type; if (!propStream.read(type)) { return nullptr; } if (!propStream.read(attr) || attr != CONDITIONATTR_ID) { return nullptr; } uint32_t id; if (!propStream.read(id)) { return nullptr; } if (!propStream.read(attr) || attr != CONDITIONATTR_TICKS) { return nullptr; } uint32_t ticks; if (!propStream.read(ticks)) { return nullptr; } if (!propStream.read(attr) || attr != CONDITIONATTR_SUBID) { return nullptr; } uint32_t subId; if (!propStream.read(subId)) { return nullptr; } return createCondition(static_cast(id), static_cast(type), ticks, 0, subId); } bool Condition::startCondition(Creature*) { if (ticks > 0) { endTime = ticks + OTSYS_TIME(); } return true; } bool Condition::isPersistent() const { if (ticks == -1) { return false; } if (!(id == CONDITIONID_DEFAULT || id == CONDITIONID_COMBAT || conditionType == CONDITION_MUTED)) { return false; } return true; } uint32_t Condition::getIcons() const { return 0; } bool Condition::updateCondition(const Condition* addCondition) { if (conditionType != addCondition->getType()) { return false; } if (ticks == -1 && addCondition->getTicks() > 0) { return false; } if (addCondition->getTicks() >= 0 && getEndTime() > (OTSYS_TIME() + addCondition->getTicks())) { return false; } return true; } bool ConditionGeneric::startCondition(Creature* creature) { return Condition::startCondition(creature); } bool ConditionGeneric::executeCondition(Creature* creature, int32_t interval) { return Condition::executeCondition(creature, interval); } void ConditionGeneric::endCondition(Creature*) { // } void ConditionGeneric::addCondition(Creature*, const Condition* addCondition) { if (updateCondition(addCondition)) { setTicks(addCondition->getTicks()); } } uint32_t ConditionGeneric::getIcons() const { uint32_t icons = Condition::getIcons(); switch (conditionType) { case CONDITION_MANASHIELD: icons |= ICON_MANASHIELD; break; case CONDITION_INFIGHT: icons |= ICON_SWORDS; break; case CONDITION_DRUNK: icons |= ICON_DRUNK; break; default: break; } return icons; } void ConditionAttributes::addCondition(Creature* creature, const Condition* addCondition) { if (updateCondition(addCondition)) { setTicks(addCondition->getTicks()); const ConditionAttributes& conditionAttrs = static_cast(*addCondition); //Remove the old condition endCondition(creature); //Apply the new one memcpy(skills, conditionAttrs.skills, sizeof(skills)); memcpy(skillsPercent, conditionAttrs.skillsPercent, sizeof(skillsPercent)); memcpy(stats, conditionAttrs.stats, sizeof(stats)); memcpy(statsPercent, conditionAttrs.statsPercent, sizeof(statsPercent)); if (Player* player = creature->getPlayer()) { updatePercentSkills(player); updateSkills(player); updatePercentStats(player); updateStats(player); } } } bool ConditionAttributes::unserializeProp(ConditionAttr_t attr, PropStream& propStream) { if (attr == CONDITIONATTR_SKILLS) { return propStream.read(skills[currentSkill++]); } else if (attr == CONDITIONATTR_STATS) { return propStream.read(stats[currentStat++]); } return Condition::unserializeProp(attr, propStream); } void ConditionAttributes::serialize(PropWriteStream& propWriteStream) { Condition::serialize(propWriteStream); for (int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { propWriteStream.write(CONDITIONATTR_SKILLS); propWriteStream.write(skills[i]); } for (int32_t i = STAT_FIRST; i <= STAT_LAST; ++i) { propWriteStream.write(CONDITIONATTR_STATS); propWriteStream.write(stats[i]); } } bool ConditionAttributes::startCondition(Creature* creature) { if (!Condition::startCondition(creature)) { return false; } if (Player* player = creature->getPlayer()) { updatePercentSkills(player); updateSkills(player); updatePercentStats(player); updateStats(player); } return true; } void ConditionAttributes::updatePercentStats(Player* player) { for (int32_t i = STAT_FIRST; i <= STAT_LAST; ++i) { if (statsPercent[i] == 0) { continue; } switch (i) { case STAT_MAXHITPOINTS: stats[i] = static_cast(player->getMaxHealth() * ((statsPercent[i] - 100) / 100.f)); break; case STAT_MAXMANAPOINTS: stats[i] = static_cast(player->getMaxMana() * ((statsPercent[i] - 100) / 100.f)); break; case STAT_MAGICPOINTS: stats[i] = static_cast(player->getMagicLevel() * ((statsPercent[i] - 100) / 100.f)); break; } } } void ConditionAttributes::updateStats(Player* player) { bool needUpdateStats = false; for (int32_t i = STAT_FIRST; i <= STAT_LAST; ++i) { if (stats[i]) { needUpdateStats = true; player->setVarStats(static_cast(i), stats[i]); } } if (needUpdateStats) { player->sendStats(); } } void ConditionAttributes::updatePercentSkills(Player* player) { for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { if (skillsPercent[i] == 0) { continue; } int32_t unmodifiedSkill = player->getBaseSkill(i); skills[i] = static_cast(unmodifiedSkill * ((skillsPercent[i] - 100) / 100.f)); } } void ConditionAttributes::updateSkills(Player* player) { bool needUpdateSkills = false; for (int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { if (skills[i]) { needUpdateSkills = true; player->setVarSkill(static_cast(i), skills[i]); } } if (needUpdateSkills) { player->sendSkills(); } } bool ConditionAttributes::executeCondition(Creature* creature, int32_t interval) { return ConditionGeneric::executeCondition(creature, interval); } void ConditionAttributes::endCondition(Creature* creature) { Player* player = creature->getPlayer(); if (player) { bool needUpdateSkills = false; for (int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { if (skills[i] || skillsPercent[i]) { needUpdateSkills = true; player->setVarSkill(static_cast(i), -skills[i]); } } if (needUpdateSkills) { player->sendSkills(); } bool needUpdateStats = false; for (int32_t i = STAT_FIRST; i <= STAT_LAST; ++i) { if (stats[i]) { needUpdateStats = true; player->setVarStats(static_cast(i), -stats[i]); } } if (needUpdateStats) { player->sendStats(); } } } bool ConditionAttributes::setParam(ConditionParam_t param, int32_t value) { bool ret = ConditionGeneric::setParam(param, value); switch (param) { case CONDITION_PARAM_SKILL_MELEE: { skills[SKILL_CLUB] = value; skills[SKILL_AXE] = value; skills[SKILL_SWORD] = value; return true; } case CONDITION_PARAM_SKILL_MELEEPERCENT: { skillsPercent[SKILL_CLUB] = value; skillsPercent[SKILL_AXE] = value; skillsPercent[SKILL_SWORD] = value; return true; } case CONDITION_PARAM_SKILL_FIST: { skills[SKILL_FIST] = value; return true; } case CONDITION_PARAM_SKILL_FISTPERCENT: { skillsPercent[SKILL_FIST] = value; return true; } case CONDITION_PARAM_SKILL_CLUB: { skills[SKILL_CLUB] = value; return true; } case CONDITION_PARAM_SKILL_CLUBPERCENT: { skillsPercent[SKILL_CLUB] = value; return true; } case CONDITION_PARAM_SKILL_SWORD: { skills[SKILL_SWORD] = value; return true; } case CONDITION_PARAM_SKILL_SWORDPERCENT: { skillsPercent[SKILL_SWORD] = value; return true; } case CONDITION_PARAM_SKILL_AXE: { skills[SKILL_AXE] = value; return true; } case CONDITION_PARAM_SKILL_AXEPERCENT: { skillsPercent[SKILL_AXE] = value; return true; } case CONDITION_PARAM_SKILL_DISTANCE: { skills[SKILL_DISTANCE] = value; return true; } case CONDITION_PARAM_SKILL_DISTANCEPERCENT: { skillsPercent[SKILL_DISTANCE] = value; return true; } case CONDITION_PARAM_SKILL_SHIELD: { skills[SKILL_SHIELD] = value; return true; } case CONDITION_PARAM_SKILL_SHIELDPERCENT: { skillsPercent[SKILL_SHIELD] = value; return true; } case CONDITION_PARAM_SKILL_FISHING: { skills[SKILL_FISHING] = value; return true; } case CONDITION_PARAM_SKILL_FISHINGPERCENT: { skillsPercent[SKILL_FISHING] = value; return true; } case CONDITION_PARAM_STAT_MAXHITPOINTS: { stats[STAT_MAXHITPOINTS] = value; return true; } case CONDITION_PARAM_STAT_MAXMANAPOINTS: { stats[STAT_MAXMANAPOINTS] = value; return true; } case CONDITION_PARAM_STAT_MAGICPOINTS: { stats[STAT_MAGICPOINTS] = value; return true; } case CONDITION_PARAM_STAT_MAXHITPOINTSPERCENT: { statsPercent[STAT_MAXHITPOINTS] = std::max(0, value); return true; } case CONDITION_PARAM_STAT_MAXMANAPOINTSPERCENT: { statsPercent[STAT_MAXMANAPOINTS] = std::max(0, value); return true; } case CONDITION_PARAM_STAT_MAGICPOINTSPERCENT: { statsPercent[STAT_MAGICPOINTS] = std::max(0, value); return true; } default: return ret; } } void ConditionRegeneration::addCondition(Creature*, const Condition* addCondition) { if (updateCondition(addCondition)) { setTicks(addCondition->getTicks()); const ConditionRegeneration& conditionRegen = static_cast(*addCondition); healthTicks = conditionRegen.healthTicks; manaTicks = conditionRegen.manaTicks; healthGain = conditionRegen.healthGain; manaGain = conditionRegen.manaGain; } } bool ConditionRegeneration::unserializeProp(ConditionAttr_t attr, PropStream& propStream) { if (attr == CONDITIONATTR_HEALTHTICKS) { return propStream.read(healthTicks); } else if (attr == CONDITIONATTR_HEALTHGAIN) { return propStream.read(healthGain); } else if (attr == CONDITIONATTR_MANATICKS) { return propStream.read(manaTicks); } else if (attr == CONDITIONATTR_MANAGAIN) { return propStream.read(manaGain); } return Condition::unserializeProp(attr, propStream); } void ConditionRegeneration::serialize(PropWriteStream& propWriteStream) { Condition::serialize(propWriteStream); propWriteStream.write(CONDITIONATTR_HEALTHTICKS); propWriteStream.write(healthTicks); propWriteStream.write(CONDITIONATTR_HEALTHGAIN); propWriteStream.write(healthGain); propWriteStream.write(CONDITIONATTR_MANATICKS); propWriteStream.write(manaTicks); propWriteStream.write(CONDITIONATTR_MANAGAIN); propWriteStream.write(manaGain); } bool ConditionRegeneration::executeCondition(Creature* creature, int32_t interval) { internalHealthTicks += interval; internalManaTicks += interval; if (creature->getZone() != ZONE_PROTECTION) { if (internalHealthTicks >= healthTicks) { internalHealthTicks = 0; creature->changeHealth(healthGain); } if (internalManaTicks >= manaTicks) { internalManaTicks = 0; if (Player* player = creature->getPlayer()) { player->changeMana(manaGain); } } } return ConditionGeneric::executeCondition(creature, interval); } bool ConditionRegeneration::setParam(ConditionParam_t param, int32_t value) { bool ret = ConditionGeneric::setParam(param, value); switch (param) { case CONDITION_PARAM_HEALTHGAIN: healthGain = value; return true; case CONDITION_PARAM_HEALTHTICKS: healthTicks = value; return true; case CONDITION_PARAM_MANAGAIN: manaGain = value; return true; case CONDITION_PARAM_MANATICKS: manaTicks = value; return true; default: return ret; } } void ConditionSoul::addCondition(Creature*, const Condition* addCondition) { if (updateCondition(addCondition)) { setTicks(addCondition->getTicks()); const ConditionSoul& conditionSoul = static_cast(*addCondition); soulTicks = conditionSoul.soulTicks; soulGain = conditionSoul.soulGain; } } bool ConditionSoul::unserializeProp(ConditionAttr_t attr, PropStream& propStream) { if (attr == CONDITIONATTR_SOULGAIN) { return propStream.read(soulGain); } else if (attr == CONDITIONATTR_SOULTICKS) { return propStream.read(soulTicks); } return Condition::unserializeProp(attr, propStream); } void ConditionSoul::serialize(PropWriteStream& propWriteStream) { Condition::serialize(propWriteStream); propWriteStream.write(CONDITIONATTR_SOULGAIN); propWriteStream.write(soulGain); propWriteStream.write(CONDITIONATTR_SOULTICKS); propWriteStream.write(soulTicks); } bool ConditionSoul::executeCondition(Creature* creature, int32_t interval) { internalSoulTicks += interval; if (Player* player = creature->getPlayer()) { if (player->getZone() != ZONE_PROTECTION) { if (internalSoulTicks >= soulTicks) { internalSoulTicks = 0; player->changeSoul(soulGain); } } } return ConditionGeneric::executeCondition(creature, interval); } bool ConditionSoul::setParam(ConditionParam_t param, int32_t value) { bool ret = ConditionGeneric::setParam(param, value); switch (param) { case CONDITION_PARAM_SOULGAIN: soulGain = value; return true; case CONDITION_PARAM_SOULTICKS: soulTicks = value; return true; default: return ret; } } bool ConditionDamage::setParam(ConditionParam_t param, int32_t value) { switch (param) { case CONDITION_PARAM_OWNER: owner = value; return true; case CONDITION_PARAM_CYCLE: cycle = value; return true; case CONDITION_PARAM_COUNT: count = value; return true; case CONDITION_PARAM_MAX_COUNT: max_count = value; return true; case CONDITION_PARAM_HIT_DAMAGE: hit_damage = value; return true; default: return false; } } bool ConditionDamage::unserializeProp(ConditionAttr_t attr, PropStream& propStream) { if (attr == CONDITIONATTR_OWNER) { return propStream.skip(4); } else if (attr == CONDITIONATTR_CYCLE) { if (!propStream.read(cycle)) { return false; } return true; } else if (attr == CONDITIONATTR_COUNT) { if (!propStream.read(count)) { return false; } return true; } else if (attr == CONDITIONATTR_MAX_COUNT) { if (!propStream.read(max_count)) { return false; } return true; } else if (attr == CONDITIONATTR_FACTOR_PERCENT) { if (!propStream.read(factor_percent)) { return false; } return true; } return Condition::unserializeProp(attr, propStream); } void ConditionDamage::serialize(PropWriteStream& propWriteStream) { Condition::serialize(propWriteStream); propWriteStream.write(CONDITIONATTR_CYCLE); propWriteStream.write(cycle); propWriteStream.write(CONDITIONATTR_COUNT); propWriteStream.write(count); propWriteStream.write(CONDITIONATTR_MAX_COUNT); propWriteStream.write(max_count); propWriteStream.write(CONDITIONATTR_FACTOR_PERCENT); propWriteStream.write(factor_percent); } bool ConditionDamage::updateCondition(const Condition* addCondition) { const ConditionDamage& conditionDamage = static_cast(*addCondition); return conditionDamage.getTotalDamage() >= getTotalDamage(); } bool ConditionDamage::startCondition(Creature* creature) { if (!Condition::startCondition(creature)) { return false; } creature->onAttacked(); setParam(CONDITION_PARAM_TICKINTERVAL, 1000); if (factor_percent == -1) { factor_percent = 50; } if (factor_percent <= 9) { factor_percent = 10; } if (factor_percent >= 1001) { factor_percent = 1000; } if (hit_damage) { doDamage(creature, -hit_damage); } return true; } bool ConditionDamage::executeCondition(Creature* creature, int32_t) { if (conditionType == CONDITION_FIRE) { if (creature->isImmune(CONDITION_FIRE)) { return false; } const int32_t r_cycle = cycle; if (r_cycle) { if (count <= 0) { count = max_count; cycle = r_cycle + 2 * (r_cycle <= 0) - 1; doDamage(creature, -10); } else { --count; } } else { return false; } } else if (conditionType == CONDITION_POISON) { if (creature->isImmune(CONDITION_POISON)) { return false; } const int32_t r_cycle = cycle; if (r_cycle) { if (count <= 0) { count = max_count; int32_t f = factor_percent * r_cycle / 1000; if (!f) { f = 2 * (r_cycle > 0) - 1; } cycle = r_cycle - f; doDamage(creature, -f); } else { --count; } } else { return false; } } else if (conditionType == CONDITION_ENERGY) { if (creature->isImmune(CONDITION_ENERGY)) { return false; } const int32_t r_cycle = cycle; if (r_cycle) { if (count <= 0) { count = max_count; cycle = r_cycle + 2 * (r_cycle <= 0) - 1; doDamage(creature, -25); } else { --count; } } else { return false; } } else if (conditionType == CONDITION_DROWN) { if (isFirstCycle && count - max_count == -2) { doDamage(creature, -20); isFirstCycle = false; count = max_count; return true; } const int32_t r_cycle = cycle; if (r_cycle) { if (count <= 0) { count = max_count; cycle = r_cycle + 2 * (r_cycle <= 0) - 1; doDamage(creature, -20); } else { --count; } } else { return false; } } return true; } bool ConditionDamage::doDamage(Creature* creature, int32_t healthChange) { if (creature->isSuppress(getType())) { return true; } CombatDamage damage; damage.origin = ORIGIN_CONDITION; damage.value = healthChange; damage.type = Combat::ConditionToDamageType(conditionType); Creature* attacker = g_game.getCreatureByID(owner); if (!creature->isAttackable() || Combat::canDoCombat(attacker, creature) != RETURNVALUE_NOERROR) { if (!creature->isInGhostMode()) { g_game.addMagicEffect(creature->getPosition(), CONST_ME_POFF); } return false; } if (g_game.combatBlockHit(damage, attacker, creature, false, false, true)) { return false; } return g_game.combatChangeHealth(attacker, creature, damage); } void ConditionDamage::endCondition(Creature*) { // } void ConditionDamage::addCondition(Creature* creature, const Condition* addCondition) { if (addCondition->getType() != conditionType) { return; } const ConditionDamage& conditionDamage = static_cast(*addCondition); if (hit_damage) { doDamage(creature, -conditionDamage.hit_damage); } if (!updateCondition(addCondition)) { return; } owner = conditionDamage.owner; cycle = conditionDamage.cycle; count = conditionDamage.count; max_count = conditionDamage.max_count; } int32_t ConditionDamage::getTotalDamage() const { return cycle; } uint32_t ConditionDamage::getIcons() const { uint32_t icons = Condition::getIcons(); switch (conditionType) { case CONDITION_FIRE: icons |= ICON_BURN; break; case CONDITION_ENERGY: icons |= ICON_ENERGY; break; case CONDITION_POISON: icons |= ICON_POISON; break; case CONDITION_DROWN: icons |= ICON_DROWNING; break; default: break; } return icons; } bool ConditionSpeed::unserializeProp(ConditionAttr_t attr, PropStream& propStream) { if (attr == CONDITIONATTR_SPEEDDELTA) { return propStream.read(speedDelta); } else if (attr == CONDITIONATTR_APPLIEDSPEEDDELTA) { return propStream.read(appliedSpeedDelta); } return Condition::unserializeProp(attr, propStream); } void ConditionSpeed::serialize(PropWriteStream& propWriteStream) { Condition::serialize(propWriteStream); propWriteStream.write(CONDITIONATTR_SPEEDDELTA); propWriteStream.write(speedDelta); propWriteStream.write(CONDITIONATTR_APPLIEDSPEEDDELTA); propWriteStream.write(appliedSpeedDelta); } bool ConditionSpeed::startCondition(Creature* creature) { if (!Condition::startCondition(creature)) { return false; } if (appliedSpeedDelta == 0) { speedDelta = normal_random(-variation, variation) + speedDelta; if (speedDelta >= -100) { speedDelta = static_cast(creature->getBaseSpeed()) * speedDelta / 100; } else { speedDelta = -20 - creature->getBaseSpeed(); } appliedSpeedDelta = speedDelta; } else { speedDelta = appliedSpeedDelta; } g_game.changeSpeed(creature, speedDelta); return true; } bool ConditionSpeed::executeCondition(Creature* creature, int32_t interval) { return Condition::executeCondition(creature, interval); } void ConditionSpeed::endCondition(Creature* creature) { g_game.changeSpeed(creature, -appliedSpeedDelta); } void ConditionSpeed::addCondition(Creature* creature, const Condition* addCondition) { if (conditionType != addCondition->getType()) { return; } if (ticks == -1 && addCondition->getTicks() > 0) { return; } const ConditionSpeed& conditionSpeed = static_cast(*addCondition); int32_t newVariation = conditionSpeed.variation; int32_t newSpeedDelta = conditionSpeed.speedDelta; newSpeedDelta = normal_random(-newVariation, newVariation) + newSpeedDelta; // update ticks setTicks(addCondition->getTicks()); if (newSpeedDelta >= -100) { newSpeedDelta = static_cast(creature->getBaseSpeed()) * newSpeedDelta / 100; } else { newSpeedDelta = -20 - creature->getBaseSpeed(); } creature->setSpeed(-appliedSpeedDelta); appliedSpeedDelta = newSpeedDelta; speedDelta = newSpeedDelta; g_game.changeSpeed(creature, newSpeedDelta); } uint32_t ConditionSpeed::getIcons() const { uint32_t icons = Condition::getIcons(); switch (conditionType) { case CONDITION_HASTE: icons |= ICON_HASTE; break; case CONDITION_PARALYZE: icons |= ICON_PARALYZE; break; default: break; } return icons; } bool ConditionInvisible::startCondition(Creature* creature) { if (!Condition::startCondition(creature)) { return false; } g_game.internalCreatureChangeVisible(creature, false); return true; } void ConditionInvisible::endCondition(Creature* creature) { if (!creature->isInvisible()) { g_game.internalCreatureChangeVisible(creature, true); } } void ConditionOutfit::setOutfit(const Outfit_t& outfit) { this->outfit = outfit; } bool ConditionOutfit::unserializeProp(ConditionAttr_t attr, PropStream& propStream) { if (attr == CONDITIONATTR_OUTFIT) { return propStream.read(outfit); } return Condition::unserializeProp(attr, propStream); } void ConditionOutfit::serialize(PropWriteStream& propWriteStream) { Condition::serialize(propWriteStream); propWriteStream.write(CONDITIONATTR_OUTFIT); propWriteStream.write(outfit); } bool ConditionOutfit::startCondition(Creature* creature) { if (!Condition::startCondition(creature)) { return false; } g_game.internalCreatureChangeOutfit(creature, outfit); return true; } bool ConditionOutfit::executeCondition(Creature* creature, int32_t interval) { return Condition::executeCondition(creature, interval); } void ConditionOutfit::endCondition(Creature* creature) { g_game.internalCreatureChangeOutfit(creature, creature->getDefaultOutfit()); } void ConditionOutfit::addCondition(Creature* creature, const Condition* addCondition) { if (updateCondition(addCondition)) { setTicks(addCondition->getTicks()); const ConditionOutfit& conditionOutfit = static_cast(*addCondition); outfit = conditionOutfit.outfit; g_game.internalCreatureChangeOutfit(creature, outfit); } } bool ConditionLight::startCondition(Creature* creature) { if (!Condition::startCondition(creature)) { return false; } internalLightTicks = 0; lightChangeInterval = ticks / lightInfo.level; creature->setCreatureLight(lightInfo); g_game.changeLight(creature); return true; } bool ConditionLight::executeCondition(Creature* creature, int32_t interval) { internalLightTicks += interval; if (internalLightTicks >= lightChangeInterval) { internalLightTicks = 0; LightInfo lightInfo = creature->getCreatureLight(); if (lightInfo.level > 0) { --lightInfo.level; creature->setCreatureLight(lightInfo); g_game.changeLight(creature); } } return Condition::executeCondition(creature, interval); } void ConditionLight::endCondition(Creature* creature) { creature->setNormalCreatureLight(); g_game.changeLight(creature); } void ConditionLight::addCondition(Creature* creature, const Condition* addCondition) { if (updateCondition(addCondition)) { setTicks(addCondition->getTicks()); const ConditionLight& conditionLight = static_cast(*addCondition); lightInfo.level = conditionLight.lightInfo.level; lightInfo.color = conditionLight.lightInfo.color; lightChangeInterval = ticks / lightInfo.level; internalLightTicks = 0; creature->setCreatureLight(lightInfo); g_game.changeLight(creature); } } bool ConditionLight::setParam(ConditionParam_t param, int32_t value) { bool ret = Condition::setParam(param, value); if (ret) { return false; } switch (param) { case CONDITION_PARAM_LIGHT_LEVEL: lightInfo.level = value; return true; case CONDITION_PARAM_LIGHT_COLOR: lightInfo.color = value; return true; default: return false; } } bool ConditionLight::unserializeProp(ConditionAttr_t attr, PropStream& propStream) { if (attr == CONDITIONATTR_LIGHTCOLOR) { uint32_t value; if (!propStream.read(value)) { return false; } lightInfo.color = value; return true; } else if (attr == CONDITIONATTR_LIGHTLEVEL) { uint32_t value; if (!propStream.read(value)) { return false; } lightInfo.level = value; return true; } else if (attr == CONDITIONATTR_LIGHTTICKS) { return propStream.read(internalLightTicks); } else if (attr == CONDITIONATTR_LIGHTINTERVAL) { return propStream.read(lightChangeInterval); } return Condition::unserializeProp(attr, propStream); } void ConditionLight::serialize(PropWriteStream& propWriteStream) { Condition::serialize(propWriteStream); // TODO: color and level could be serialized as 8-bit if we can retain backwards // compatibility, but perhaps we should keep it like this in case they increase // in the future... propWriteStream.write(CONDITIONATTR_LIGHTCOLOR); propWriteStream.write(lightInfo.color); propWriteStream.write(CONDITIONATTR_LIGHTLEVEL); propWriteStream.write(lightInfo.level); propWriteStream.write(CONDITIONATTR_LIGHTTICKS); propWriteStream.write(internalLightTicks); propWriteStream.write(CONDITIONATTR_LIGHTINTERVAL); propWriteStream.write(lightChangeInterval); }