diff --git a/data/lib/compat/compat.lua b/data/lib/compat/compat.lua index e518e6f112..0beaba1a27 100644 --- a/data/lib/compat/compat.lua +++ b/data/lib/compat/compat.lua @@ -311,6 +311,11 @@ setCombatCondition = function(...) Combat.addCondition(...) end +function doTargetCombatHealth(...) return doTargetCombat(...) end +function doAreaCombatHealth(...) return doAreaCombat(...) end +function doTargetCombatMana(cid, target, min, max, effect) return doTargetCombat(cid, target, COMBAT_MANADRAIN, min, max, effect) end +function doAreaCombatMana(cid, pos, area, min, max, effect) return doAreaCombat(cid, COMBAT_MANADRAIN, pos, area, min, max, effect) end + createConditionObject = Condition setConditionParam = Condition.setParameter setConditionFormula = Condition.setFormula diff --git a/src/combat.cpp b/src/combat.cpp index 22651450a7..85180fc042 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -478,74 +478,6 @@ CallBack* Combat::getCallback(CallBackParam_t key) return nullptr; } -void Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data) -{ - assert(data); - CombatDamage damage = *data; - if (g_game.combatBlockHit(damage, caster, target, params.blockedByShield, params.blockedByArmor, params.itemId != 0)) { - return; - } - - if ((damage.primary.value < 0 || damage.secondary.value < 0) && caster) { - Player* targetPlayer = target->getPlayer(); - if (targetPlayer && caster->getPlayer() && targetPlayer->getSkull() != SKULL_BLACK) { - damage.primary.value /= 2; - damage.secondary.value /= 2; - } - } - - if (g_game.combatChangeHealth(caster, target, damage)) { - CombatConditionFunc(caster, target, params, &damage); - CombatDispelFunc(caster, target, params, nullptr); - } -} - -void Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage) -{ - assert(damage); - CombatDamage damageCopy = *damage; - if (damageCopy.primary.value < 0) { - if (caster && caster->getPlayer() && target->getPlayer()) { - damageCopy.primary.value /= 2; - } - } - - if (g_game.combatChangeMana(caster, target, damageCopy)) { - CombatConditionFunc(caster, target, params, nullptr); - CombatDispelFunc(caster, target, params, nullptr); - } -} - -void Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data) -{ - if (params.origin == ORIGIN_MELEE && data && data->primary.value == 0 && data->secondary.value == 0) { - return; - } - - for (const auto& condition : params.conditionList) { - if (caster == target || !target->isImmune(condition->getType())) { - Condition* conditionCopy = condition->clone(); - if (caster) { - conditionCopy->setParam(CONDITION_PARAM_OWNER, caster->getID()); - } - - //TODO: infight condition until all aggressive conditions has ended - target->addCombatCondition(conditionCopy); - } - } -} - -void Combat::CombatDispelFunc(Creature*, Creature* target, const CombatParams& params, CombatDamage*) -{ - target->removeCombatCondition(params.dispelType); -} - -void Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage*) -{ - CombatConditionFunc(caster, target, params, nullptr); - CombatDispelFunc(caster, target, params, nullptr); -} - void Combat::combatTileEffects(const SpectatorVec& spectators, Creature* caster, Tile* tile, const CombatParams& params) { if (params.itemId != 0) { @@ -668,14 +600,227 @@ void Combat::addDistanceEffect(Creature* caster, const Position& fromPos, const } } -void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, CombatFunction func, CombatDamage* data) +void Combat::doCombat(Creature* caster, Creature* target) const +{ + //target combat callback function + if (params.combatType != COMBAT_NONE) { + CombatDamage damage = getCombatDamage(caster, target); + + bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target) == RETURNVALUE_NOERROR); + if ((caster == target || canCombat) && params.impactEffect != CONST_ME_NONE) { + g_game.addMagicEffect(target->getPosition(), params.impactEffect); + } + + if (canCombat) { + doTargetCombat(caster, target, damage, params); + } + } else { + if (!params.aggressive || (caster != target && Combat::canDoCombat(caster, target) == RETURNVALUE_NOERROR)) { + SpectatorVec spectators; + g_game.map.getSpectators(spectators, target->getPosition(), true, true); + + if (params.origin != ORIGIN_MELEE) { + for (const auto& condition : params.conditionList) { + if (caster == target || !target->isImmune(condition->getType())) { + Condition* conditionCopy = condition->clone(); + conditionCopy->setParam(CONDITION_PARAM_OWNER, caster->getID()); + target->addCombatCondition(conditionCopy); + } + } + } + + if (params.dispelType == CONDITION_PARALYZE) { + target->removeCondition(CONDITION_PARALYZE); + } else { + target->removeCombatCondition(params.dispelType); + } + + combatTileEffects(spectators, caster, target->getTile(), params); + + if (params.targetCallback) { + params.targetCallback->onTargetCombat(caster, target); + } + + /* + if (params.impactEffect != CONST_ME_NONE) { + g_game.addMagicEffect(target->getPosition(), params.impactEffect); + } + */ + + if (caster && params.distanceEffect != CONST_ANI_NONE) { + addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); + } + } + } +} + +void Combat::doCombat(Creature* caster, const Position& position) const +{ + //area combat callback function + if (params.combatType != COMBAT_NONE) { + CombatDamage damage = getCombatDamage(caster, nullptr); + doAreaCombat(caster, position, area.get(), damage, params); + } else { + std::forward_list tileList; + + if (caster) { + getCombatArea(caster->getPosition(), position, area.get(), tileList); + } else { + getCombatArea(position, position, area.get(), tileList); + } + + SpectatorVec spectators; + uint32_t maxX = 0; + uint32_t maxY = 0; + + //calculate the max viewable range + for (Tile* tile : tileList) { + const Position& tilePos = tile->getPosition(); + + uint32_t diff = Position::getDistanceX(tilePos, position); + if (diff > maxX) { + maxX = diff; + } + + diff = Position::getDistanceY(tilePos, position); + if (diff > maxY) { + maxY = diff; + } + } + + const int32_t rangeX = maxX + Map::maxViewportX; + const int32_t rangeY = maxY + Map::maxViewportY; + g_game.map.getSpectators(spectators, position, true, true, rangeX, rangeX, rangeY, rangeY); + + postCombatEffects(caster, position, params); + + for (Tile* tile : tileList) { + if (canDoCombat(caster, tile, params.aggressive) != RETURNVALUE_NOERROR) { + continue; + } + + combatTileEffects(spectators, caster, tile, params); + + if (CreatureVector* creatures = tile->getCreatures()) { + const Creature* topCreature = tile->getTopCreature(); + for (Creature* creature : *creatures) { + if (params.targetCasterOrTopMost) { + if (caster && caster->getTile() == tile) { + if (creature != caster) { + continue; + } + } else if (creature != topCreature) { + continue; + } + } + + if (params.origin != ORIGIN_MELEE) { + for (const auto& condition : params.conditionList) { + if (caster == creature || !creature->isImmune(condition->getType())) { + Condition* conditionCopy = condition->clone(); + conditionCopy->setParam(CONDITION_PARAM_OWNER, caster->getID()); + + //TODO: infight condition until all aggressive conditions has ended + creature->addCombatCondition(conditionCopy); + } + } + } + + if (params.dispelType == CONDITION_PARALYZE) { + creature->removeCondition(CONDITION_PARALYZE); + } else { + creature->removeCombatCondition(params.dispelType); + } + } + } + } + } +} + +void Combat::doTargetCombat(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params) +{ + Player* casterPlayer = caster ? caster->getPlayer() : nullptr; + if (casterPlayer) { + if (damage.primary.value < 0 || damage.secondary.value < 0) { + Player* targetPlayer = target ? target->getPlayer() : nullptr; + if (targetPlayer && targetPlayer->getSkull() != SKULL_BLACK) { + damage.primary.value /= 2; + damage.secondary.value /= 2; + } + + Combat::checkCriticalHit(casterPlayer, damage); + Combat::checkLeech(casterPlayer, damage); + } + } + + if (caster && target && params.distanceEffect != CONST_ANI_NONE) { + addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); + } + + if (damage.critical && target) { + g_game.addMagicEffect(target->getPosition(), CONST_ME_CRITICAL_DAMAGE); + } + + bool success = false; + if (damage.primary.type != COMBAT_MANADRAIN) { + if (g_game.combatBlockHit(damage, caster, target, params.blockedByShield, params.blockedByArmor, params.itemId != 0)) { + return; + } + + success = g_game.combatChangeHealth(caster, target, damage); + } else { + success = g_game.combatChangeMana(caster, target, damage); + } + + if (success) { + if (params.origin != ORIGIN_MELEE && damage.primary.value != 0 && damage.secondary.value != 0) { + for (const auto& condition : params.conditionList) { + if (caster == target || !target->isImmune(condition->getType())) { + Condition* conditionCopy = condition->clone(); + conditionCopy->setParam(CONDITION_PARAM_OWNER, caster->getID()); + + //TODO: infight condition until all aggressive conditions has ended + target->addCombatCondition(conditionCopy); + } + } + } + + if (params.dispelType == CONDITION_PARALYZE) { + target->removeCondition(CONDITION_PARALYZE); + } else { + target->removeCombatCondition(params.dispelType); + } + } + + if (params.targetCallback) { + params.targetCallback->onTargetCombat(caster, target); + } +} + +void Combat::doAreaCombat(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params) { std::forward_list tileList; if (caster) { - getCombatArea(caster->getPosition(), pos, area, tileList); + getCombatArea(caster->getPosition(), position, area, tileList); } else { - getCombatArea(pos, pos, area, tileList); + getCombatArea(position, position, area, tileList); + } + + Player* casterPlayer = caster ? caster->getPlayer() : nullptr; + int32_t criticalPrimary = 0; + int32_t criticalSecondary = 0; + if (casterPlayer) { + Combat::checkLeech(casterPlayer, damage); + if (!damage.critical && damage.origin != ORIGIN_CONDITION && (damage.primary.value < 0 || damage.secondary.value < 0)) { + uint16_t chance = casterPlayer->getSpecialSkill(SPECIALSKILL_CRITICALHITCHANCE); + if (chance != 0 && uniform_random(1, 100) <= chance) { + uint16_t criticalHit = casterPlayer->getSpecialSkill(SPECIALSKILL_CRITICALHITAMOUNT); + criticalPrimary = std::round(damage.primary.value * (criticalHit / 100.)); + criticalSecondary = std::round(damage.secondary.value * (criticalHit / 100.)); + damage.critical = true; + } + } } uint32_t maxX = 0; @@ -685,12 +830,12 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* for (Tile* tile : tileList) { const Position& tilePos = tile->getPosition(); - uint32_t diff = Position::getDistanceX(tilePos, pos); + uint32_t diff = Position::getDistanceX(tilePos, position); if (diff > maxX) { maxX = diff; } - diff = Position::getDistanceY(tilePos, pos); + diff = Position::getDistanceY(tilePos, position); if (diff > maxY) { maxY = diff; } @@ -700,9 +845,9 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* const int32_t rangeY = maxY + Map::maxViewportY; SpectatorVec spectators; - g_game.map.getSpectators(spectators, pos, true, true, rangeX, rangeX, rangeY, rangeY); + g_game.map.getSpectators(spectators, position, true, true, rangeX, rangeX, rangeY, rangeY); - postCombatEffects(caster, pos, params); + postCombatEffects(caster, position, params); for (Tile* tile : tileList) { if (canDoCombat(caster, tile, params.aggressive) != RETURNVALUE_NOERROR) { @@ -725,191 +870,117 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* } if (!params.aggressive || (caster != creature && Combat::canDoCombat(caster, creature) == RETURNVALUE_NOERROR)) { - func(caster, creature, params, data); - if (params.targetCallback) { - params.targetCallback->onTargetCombat(caster, creature); + CombatDamage damageCopy = damage; // we cannot avoid copying here, because we don't know if it's player combat or not, so we can't modify the initial damage. + bool playerCombatReduced = false; + if ((damageCopy.primary.value < 0 || damageCopy.secondary.value < 0) && caster) { + Player* targetPlayer = creature->getPlayer(); + if (targetPlayer && caster->getPlayer() && targetPlayer->getSkull() != SKULL_BLACK) { + damageCopy.primary.value /= 2; + damageCopy.secondary.value /= 2; + playerCombatReduced = true; + } } - if (params.targetCasterOrTopMost) { - break; + damageCopy.primary.value += playerCombatReduced ? criticalPrimary / 2 : criticalPrimary; + damageCopy.secondary.value += playerCombatReduced ? criticalSecondary / 2 : criticalSecondary; + + if (damageCopy.critical) { + g_game.addMagicEffect(creature->getPosition(), CONST_ME_CRITICAL_DAMAGE); } - } - } - } - } -} -void Combat::doCombat(Creature* caster, Creature* target) const -{ - //target combat callback function - if (params.combatType != COMBAT_NONE) { - CombatDamage damage = getCombatDamage(caster, target); - if (damage.primary.type != COMBAT_MANADRAIN) { - doCombatHealth(caster, target, damage, params); - } else { - doCombatMana(caster, target, damage, params); - } - } else { - doCombatDefault(caster, target, params); - } + bool success = false; + if (damageCopy.primary.type != COMBAT_MANADRAIN) { + if (g_game.combatBlockHit(damageCopy, caster, creature, params.blockedByShield, params.blockedByArmor, params.itemId != 0)) { + continue; + } + success = g_game.combatChangeHealth(caster, creature, damageCopy); + } else { + success = g_game.combatChangeMana(caster, creature, damageCopy); + } - if (params.useCharges) { - if (Player* casterPlayer = caster->getPlayer()) { - Item* tool = casterPlayer->getWeapon(); - if (tool) { - uint16_t charges = tool->getCharges(); - if (charges != 0) { - g_game.transformItem(tool, tool->getID(), charges - 1); - } - } - } - } -} + if (success) { + if (params.origin != ORIGIN_MELEE && damageCopy.primary.value != 0 && damageCopy.secondary.value != 0) { + for (const auto& condition : params.conditionList) { + if (caster == creature || !creature->isImmune(condition->getType())) { + Condition* conditionCopy = condition->clone(); + conditionCopy->setParam(CONDITION_PARAM_OWNER, caster->getID()); + + //TODO: infight condition until all aggressive conditions has ended + creature->addCombatCondition(conditionCopy); + } + } + } -void Combat::doCombat(Creature* caster, const Position& position) const -{ - //area combat callback function - if (params.combatType != COMBAT_NONE) { - CombatDamage damage = getCombatDamage(caster, nullptr); - if (damage.primary.type != COMBAT_MANADRAIN) { - doCombatHealth(caster, position, area.get(), damage, params); - } else { - doCombatMana(caster, position, area.get(), damage, params); - } - } else { - CombatFunc(caster, position, area.get(), params, CombatNullFunc, nullptr); - } + if (params.dispelType == CONDITION_PARALYZE) { + creature->removeCondition(CONDITION_PARALYZE); + } else { + creature->removeCombatCondition(params.dispelType); + } + } + + if (params.targetCallback) { + params.targetCallback->onTargetCombat(caster, creature); + } - if (params.useCharges) { - if (Player* casterPlayer = caster->getPlayer()) { - Item* tool = casterPlayer->getWeapon(); - if (tool) { - uint16_t charges = tool->getCharges(); - if (charges != 0) { - g_game.transformItem(tool, tool->getID(), charges - 1); + if (params.targetCasterOrTopMost) { + break; + } } } } } } -void Combat::doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params) +void Combat::checkCriticalHit(Player* caster, CombatDamage& damage) { - bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target) == RETURNVALUE_NOERROR); - if ((caster == target || canCombat) && params.impactEffect != CONST_ME_NONE) { - g_game.addMagicEffect(target->getPosition(), params.impactEffect); - } - - if (canCombat) { - if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); - } - - CombatHealthFunc(caster, target, params, &damage); - if (params.targetCallback) { - params.targetCallback->onTargetCombat(caster, target); - } + if (damage.critical || damage.origin == ORIGIN_CONDITION) { + return; } -} -void Combat::doCombatHealth(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params) -{ - CombatFunc(caster, position, area, params, CombatHealthFunc, &damage); -} - -void Combat::doCombatMana(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params) -{ - bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target) == RETURNVALUE_NOERROR); - if ((caster == target || canCombat) && params.impactEffect != CONST_ME_NONE) { - g_game.addMagicEffect(target->getPosition(), params.impactEffect); + if (damage.primary.value > 0 || damage.secondary.value > 0) { + return; } - if (canCombat) { - if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); - } - - CombatManaFunc(caster, target, params, &damage); - if (params.targetCallback) { - params.targetCallback->onTargetCombat(caster, target); - } + uint16_t chance = caster->getSpecialSkill(SPECIALSKILL_CRITICALHITCHANCE); + uint16_t criticalHit = caster->getSpecialSkill(SPECIALSKILL_CRITICALHITAMOUNT); + if (criticalHit != 0 && chance != 0 && normal_random(1, 100) <= chance) { + damage.primary.value += std::round(damage.primary.value * (criticalHit / 100.)); + damage.secondary.value += std::round(damage.secondary.value * (criticalHit / 100.)); + damage.critical = true; } } -void Combat::doCombatMana(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params) -{ - CombatFunc(caster, position, area, params, CombatManaFunc, &damage); -} - -void Combat::doCombatCondition(Creature* caster, const Position& position, const AreaCombat* area, const CombatParams& params) +void Combat::checkLeech(Player* caster, CombatDamage& damage) { - CombatFunc(caster, position, area, params, CombatConditionFunc, nullptr); -} - -void Combat::doCombatCondition(Creature* caster, Creature* target, const CombatParams& params) -{ - bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target) == RETURNVALUE_NOERROR); - if ((caster == target || canCombat) && params.impactEffect != CONST_ME_NONE) { - g_game.addMagicEffect(target->getPosition(), params.impactEffect); - } - - if (canCombat) { - if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); - } - - CombatConditionFunc(caster, target, params, nullptr); - if (params.targetCallback) { - params.targetCallback->onTargetCombat(caster, target); - } + if (damage.origin == ORIGIN_CONDITION) { + return; } -} -void Combat::doCombatDispel(Creature* caster, const Position& position, const AreaCombat* area, const CombatParams& params) -{ - CombatFunc(caster, position, area, params, CombatDispelFunc, nullptr); -} - -void Combat::doCombatDispel(Creature* caster, Creature* target, const CombatParams& params) -{ - bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target) == RETURNVALUE_NOERROR); - if ((caster == target || canCombat) && params.impactEffect != CONST_ME_NONE) { - g_game.addMagicEffect(target->getPosition(), params.impactEffect); + if (damage.primary.value > 0 || damage.secondary.value > 0) { + return; } - if (canCombat) { - CombatDispelFunc(caster, target, params, nullptr); - if (params.targetCallback) { - params.targetCallback->onTargetCombat(caster, target); - } - - if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); + if (caster->getHealth() != caster->getMaxHealth()) { + uint16_t chance = caster->getSpecialSkill(SPECIALSKILL_LIFELEECHCHANCE); + uint16_t skill = caster->getSpecialSkill(SPECIALSKILL_LIFELEECHAMOUNT); + if (skill != 0 && chance != 0 && normal_random(1, 100) <= chance) { + CombatDamage healAmount; + healAmount.primary.value += std::round(std::abs(damage.primary.value) * (skill / 100.)); + healAmount.secondary.value += std::round(std::abs(damage.secondary.value) * (skill / 100.)); + g_game.combatChangeHealth(nullptr, caster, healAmount); + caster->sendMagicEffect(caster->getPosition(), CONST_ME_MAGIC_RED); } } -} - -void Combat::doCombatDefault(Creature* caster, Creature* target, const CombatParams& params) -{ - if (!params.aggressive || (caster != target && Combat::canDoCombat(caster, target) == RETURNVALUE_NOERROR)) { - SpectatorVec spectators; - g_game.map.getSpectators(spectators, target->getPosition(), true, true); - - CombatNullFunc(caster, target, params, nullptr); - combatTileEffects(spectators, caster, target->getTile(), params); - - if (params.targetCallback) { - params.targetCallback->onTargetCombat(caster, target); - } - - /* - if (params.impactEffect != CONST_ME_NONE) { - g_game.addMagicEffect(target->getPosition(), params.impactEffect); - } - */ - if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); + if (caster->getMana() != caster->getMaxMana()) { + uint16_t chance = caster->getSpecialSkill(SPECIALSKILL_MANALEECHCHANCE); + uint16_t skill = caster->getSpecialSkill(SPECIALSKILL_MANALEECHAMOUNT); + if (skill != 0 && chance != 0 && normal_random(1, 100) <= chance) { + CombatDamage manaAmount; + manaAmount.primary.value += std::round(std::abs(damage.primary.value) * (skill / 100.)); + manaAmount.secondary.value += std::round(std::abs(damage.secondary.value) * (skill / 100.)); + g_game.combatChangeMana(nullptr, caster, manaAmount); + caster->sendMagicEffect(caster->getPosition(), CONST_ME_MAGIC_BLUE); } } } diff --git a/src/combat.h b/src/combat.h index 275fa6ac16..92fef82969 100644 --- a/src/combat.h +++ b/src/combat.h @@ -77,8 +77,6 @@ struct CombatParams { bool useCharges = false; }; -using CombatFunction = std::function; - class MatrixArea { public: @@ -242,18 +240,6 @@ class Combat Combat(const Combat&) = delete; Combat& operator=(const Combat&) = delete; - static void doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params); - static void doCombatHealth(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params); - - static void doCombatMana(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params); - static void doCombatMana(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params); - - static void doCombatCondition(Creature* caster, Creature* target, const CombatParams& params); - static void doCombatCondition(Creature* caster, const Position& position, const AreaCombat* area, const CombatParams& params); - - static void doCombatDispel(Creature* caster, Creature* target, const CombatParams& params); - static void doCombatDispel(Creature* caster, const Position& position, const AreaCombat* area, const CombatParams& params); - static void getCombatArea(const Position& centerPos, const Position& targetPos, const AreaCombat* area, std::forward_list& list); static bool isInPvpZone(const Creature* attacker, const Creature* target); @@ -271,6 +257,12 @@ class Combat void doCombat(Creature* caster, Creature* target) const; void doCombat(Creature* caster, const Position& position) const; + static void doTargetCombat(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params); + static void doAreaCombat(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params); + + static void checkCriticalHit(Player* caster, CombatDamage& damage); + static void checkLeech(Player* caster, CombatDamage& damage); + bool setCallback(CallBackParam_t key); CallBack* getCallback(CallBackParam_t key); @@ -297,16 +289,6 @@ class Combat } private: - static void doCombatDefault(Creature* caster, Creature* target, const CombatParams& params); - - static void CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, CombatFunction func, CombatDamage* data); - - static void CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); - static void CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage); - static void CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); - static void CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); - static void CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); - static void combatTileEffects(const SpectatorVec& spectators, Creature* caster, Tile* tile, const CombatParams& params); CombatDamage getCombatDamage(Creature* creature, Creature* target) const; diff --git a/src/enums.h b/src/enums.h index 1f720dcd0b..4a81fff430 100644 --- a/src/enums.h +++ b/src/enums.h @@ -605,11 +605,13 @@ struct CombatDamage } primary, secondary; CombatOrigin origin; + bool critical; CombatDamage() { origin = ORIGIN_NONE; primary.type = secondary.type = COMBAT_NONE; primary.value = secondary.value = 0; + critical = false; } }; diff --git a/src/game.cpp b/src/game.cpp index cc284b603e..db513e2982 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4019,28 +4019,6 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage return true; } - if (attackerPlayer) { - uint16_t chance = attackerPlayer->getSpecialSkill(SPECIALSKILL_LIFELEECHCHANCE); - if (chance != 0 && uniform_random(1, 100) <= chance) { - CombatDamage lifeLeech; - lifeLeech.primary.value = std::round(healthChange * (attackerPlayer->getSpecialSkill(SPECIALSKILL_LIFELEECHAMOUNT) / 100.)); - combatChangeHealth(nullptr, attackerPlayer, lifeLeech); - } - - chance = attackerPlayer->getSpecialSkill(SPECIALSKILL_MANALEECHCHANCE); - if (chance != 0 && uniform_random(1, 100) <= chance) { - CombatDamage manaLeech; - manaLeech.primary.value = std::round(healthChange * (attackerPlayer->getSpecialSkill(SPECIALSKILL_MANALEECHAMOUNT) / 100.)); - combatChangeMana(nullptr, attackerPlayer, manaLeech); - } - - chance = attackerPlayer->getSpecialSkill(SPECIALSKILL_CRITICALHITCHANCE); - if (chance != 0 && uniform_random(1, 100) <= chance) { - healthChange += std::round(healthChange * (attackerPlayer->getSpecialSkill(SPECIALSKILL_CRITICALHITAMOUNT) / 100.)); - addMagicEffect(target->getPosition(), CONST_ME_CRITICAL_DAMAGE); - } - } - TextMessage message; message.position = targetPos; diff --git a/src/luascript.cpp b/src/luascript.cpp index 60a37311d1..c632624d22 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -1010,29 +1010,11 @@ void LuaScriptInterface::registerFunctions() //createCombatArea( {area}, {extArea} ) lua_register(luaState, "createCombatArea", LuaScriptInterface::luaCreateCombatArea); - //doAreaCombatHealth(cid, type, pos, area, min, max, effect) - lua_register(luaState, "doAreaCombatHealth", LuaScriptInterface::luaDoAreaCombatHealth); + //doAreaCombat(cid, type, pos, area, min, max, effect) + lua_register(luaState, "doAreaCombat", LuaScriptInterface::luaDoAreaCombat); - //doTargetCombatHealth(cid, target, type, min, max, effect) - lua_register(luaState, "doTargetCombatHealth", LuaScriptInterface::luaDoTargetCombatHealth); - - //doAreaCombatMana(cid, pos, area, min, max, effect) - lua_register(luaState, "doAreaCombatMana", LuaScriptInterface::luaDoAreaCombatMana); - - //doTargetCombatMana(cid, target, min, max, effect) - lua_register(luaState, "doTargetCombatMana", LuaScriptInterface::luaDoTargetCombatMana); - - //doAreaCombatCondition(cid, pos, area, condition, effect) - lua_register(luaState, "doAreaCombatCondition", LuaScriptInterface::luaDoAreaCombatCondition); - - //doTargetCombatCondition(cid, target, condition, effect) - lua_register(luaState, "doTargetCombatCondition", LuaScriptInterface::luaDoTargetCombatCondition); - - //doAreaCombatDispel(cid, pos, area, type, effect) - lua_register(luaState, "doAreaCombatDispel", LuaScriptInterface::luaDoAreaCombatDispel); - - //doTargetCombatDispel(cid, target, type, effect) - lua_register(luaState, "doTargetCombatDispel", LuaScriptInterface::luaDoTargetCombatDispel); + //doTargetCombat(cid, target, type, min, max, effect) + lua_register(luaState, "doTargetCombat", LuaScriptInterface::luaDoTargetCombat); //doChallengeCreature(cid, target) lua_register(luaState, "doChallengeCreature", LuaScriptInterface::luaDoChallengeCreature); @@ -3263,9 +3245,9 @@ int LuaScriptInterface::luaCreateCombatArea(lua_State* L) return 1; } -int LuaScriptInterface::luaDoAreaCombatHealth(lua_State* L) +int LuaScriptInterface::luaDoAreaCombat(lua_State* L) { - //doAreaCombatHealth(cid, type, pos, area, min, max, effect[, origin = ORIGIN_SPELL]) + //doAreaCombat(cid, type, pos, area, min, max, effect[, origin = ORIGIN_SPELL]) Creature* creature = getCreature(L, 1); if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); @@ -3287,7 +3269,7 @@ int LuaScriptInterface::luaDoAreaCombatHealth(lua_State* L) damage.primary.type = combatType; damage.primary.value = normal_random(getNumber(L, 6), getNumber(L, 5)); - Combat::doCombatHealth(creature, getPosition(L, 3), area, damage, params); + Combat::doAreaCombat(creature, getPosition(L, 3), area, damage, params); pushBoolean(L, true); } else { reportErrorFunc(getErrorDesc(LUA_ERROR_AREA_NOT_FOUND)); @@ -3296,9 +3278,9 @@ int LuaScriptInterface::luaDoAreaCombatHealth(lua_State* L) return 1; } -int LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L) +int LuaScriptInterface::luaDoTargetCombat(lua_State* L) { - //doTargetCombatHealth(cid, target, type, min, max, effect[, origin = ORIGIN_SPELL]) + //doTargetCombat(cid, target, type, min, max, effect[, origin = ORIGIN_SPELL]) Creature* creature = getCreature(L, 1); if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); @@ -3324,183 +3306,7 @@ int LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L) damage.primary.type = combatType; damage.primary.value = normal_random(getNumber(L, 4), getNumber(L, 5)); - Combat::doCombatHealth(creature, target, damage, params); - pushBoolean(L, true); - return 1; -} - -int LuaScriptInterface::luaDoAreaCombatMana(lua_State* L) -{ - //doAreaCombatMana(cid, pos, area, min, max, effect[, origin = ORIGIN_SPELL]) - Creature* creature = getCreature(L, 1); - if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - uint32_t areaId = getNumber(L, 3); - const AreaCombat* area = g_luaEnvironment.getAreaObject(areaId); - if (area || areaId == 0) { - CombatParams params; - params.impactEffect = getNumber(L, 6); - - CombatDamage damage; - damage.origin = getNumber(L, 7, ORIGIN_SPELL); - damage.primary.type = COMBAT_MANADRAIN; - damage.primary.value = normal_random(getNumber(L, 4), getNumber(L, 5)); - - Position pos = getPosition(L, 2); - Combat::doCombatMana(creature, pos, area, damage, params); - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_AREA_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} - -int LuaScriptInterface::luaDoTargetCombatMana(lua_State* L) -{ - //doTargetCombatMana(cid, target, min, max, effect[, origin = ORIGIN_SPELL) - Creature* creature = getCreature(L, 1); - if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - Creature* target = getCreature(L, 2); - if (!target) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - CombatParams params; - params.impactEffect = getNumber(L, 5); - - CombatDamage damage; - damage.origin = getNumber(L, 6, ORIGIN_SPELL); - damage.primary.type = COMBAT_MANADRAIN; - damage.primary.value = normal_random(getNumber(L, 3), getNumber(L, 4)); - - Combat::doCombatMana(creature, target, damage, params); - pushBoolean(L, true); - return 1; -} - -int LuaScriptInterface::luaDoAreaCombatCondition(lua_State* L) -{ - //doAreaCombatCondition(cid, pos, area, condition, effect) - Creature* creature = getCreature(L, 1); - if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - const Condition* condition = getUserdata(L, 4); - if (!condition) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CONDITION_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - uint32_t areaId = getNumber(L, 3); - const AreaCombat* area = g_luaEnvironment.getAreaObject(areaId); - if (area || areaId == 0) { - CombatParams params; - params.impactEffect = getNumber(L, 5); - params.conditionList.emplace_front(condition->clone()); - Combat::doCombatCondition(creature, getPosition(L, 2), area, params); - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_AREA_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} - -int LuaScriptInterface::luaDoTargetCombatCondition(lua_State* L) -{ - //doTargetCombatCondition(cid, target, condition, effect) - Creature* creature = getCreature(L, 1); - if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - Creature* target = getCreature(L, 2); - if (!target) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - const Condition* condition = getUserdata(L, 3); - if (!condition) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CONDITION_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - CombatParams params; - params.impactEffect = getNumber(L, 4); - params.conditionList.emplace_front(condition->clone()); - Combat::doCombatCondition(creature, target, params); - pushBoolean(L, true); - return 1; -} - -int LuaScriptInterface::luaDoAreaCombatDispel(lua_State* L) -{ - //doAreaCombatDispel(cid, pos, area, type, effect) - Creature* creature = getCreature(L, 1); - if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - uint32_t areaId = getNumber(L, 3); - const AreaCombat* area = g_luaEnvironment.getAreaObject(areaId); - if (area || areaId == 0) { - CombatParams params; - params.impactEffect = getNumber(L, 5); - params.dispelType = getNumber(L, 4); - Combat::doCombatDispel(creature, getPosition(L, 2), area, params); - - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_AREA_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} - -int LuaScriptInterface::luaDoTargetCombatDispel(lua_State* L) -{ - //doTargetCombatDispel(cid, target, type, effect) - Creature* creature = getCreature(L, 1); - if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - Creature* target = getCreature(L, 2); - if (!target) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - CombatParams params; - params.dispelType = getNumber(L, 3); - params.impactEffect = getNumber(L, 4); - Combat::doCombatDispel(creature, target, params); + Combat::doTargetCombat(creature, target, damage, params); pushBoolean(L, true); return 1; } diff --git a/src/luascript.h b/src/luascript.h index 4efe4b9c74..f176969ed7 100644 --- a/src/luascript.h +++ b/src/luascript.h @@ -460,18 +460,8 @@ class LuaScriptInterface // static int luaCreateCombatArea(lua_State* L); - static int luaDoAreaCombatHealth(lua_State* L); - static int luaDoTargetCombatHealth(lua_State* L); - - // - static int luaDoAreaCombatMana(lua_State* L); - static int luaDoTargetCombatMana(lua_State* L); - - static int luaDoAreaCombatCondition(lua_State* L); - static int luaDoTargetCombatCondition(lua_State* L); - - static int luaDoAreaCombatDispel(lua_State* L); - static int luaDoTargetCombatDispel(lua_State* L); + static int luaDoAreaCombat(lua_State* L); + static int luaDoTargetCombat(lua_State* L); static int luaDoChallengeCreature(lua_State* L); diff --git a/src/weapons.cpp b/src/weapons.cpp index 2452f8aca4..dcbe3fd78c 100644 --- a/src/weapons.cpp +++ b/src/weapons.cpp @@ -367,7 +367,7 @@ bool Weapon::useFist(Player* player, Creature* target) damage.primary.type = params.combatType; damage.primary.value = -normal_random(0, maxDamage); - Combat::doCombatHealth(player, target, damage, params); + Combat::doTargetCombat(player, target, damage, params); if (!player->hasFlag(PlayerFlag_NotGainSkill) && player->getAddAttackSkill()) { player->addSkillAdvance(SKILL_FIST, 1); } @@ -394,7 +394,7 @@ void Weapon::internalUseWeapon(Player* player, Item* item, Creature* target, int damage.primary.value = (getWeaponDamage(player, target, item) * damageModifier) / 100; damage.secondary.type = getElementType(); damage.secondary.value = getElementDamage(player, target, item); - Combat::doCombatHealth(player, target, damage, params); + Combat::doTargetCombat(player, target, damage, params); } onUsedWeapon(player, item, target->getTile());