diff --git a/config.lua.dist b/config.lua.dist index fec7fb25e72..da9d4fb085f 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -147,6 +147,7 @@ hazardCriticalInterval = 2000 hazardCriticalChance = 750 hazardCriticalMultiplier = 25 hazardDamageMultiplier = 200 +hazardDefenseMultiplier = 0 hazardDodgeMultiplier = 85 hazardPodsDropMultiplier = 87 hazardPodsTimeToDamage = 2000 diff --git a/data-otservbr-global/scripts/hazard/primal.lua b/data-otservbr-global/scripts/hazard/primal.lua index 2b8fd67a25d..ef9a9c31ca5 100644 --- a/data-otservbr-global/scripts/hazard/primal.lua +++ b/data-otservbr-global/scripts/hazard/primal.lua @@ -7,6 +7,7 @@ local hazard = Hazard.new({ crit = true, dodge = true, damageBoost = true, + defenseBoost = true, }) hazard:register() diff --git a/data/libs/hazard_lib.lua b/data/libs/hazard_lib.lua index c13f120f740..f9338299e79 100644 --- a/data/libs/hazard_lib.lua +++ b/data/libs/hazard_lib.lua @@ -8,12 +8,14 @@ function Hazard.new(prototype) instance.name = prototype.name instance.from = prototype.from instance.to = prototype.to + instance.minLevel = prototype.minLevel or 1 instance.maxLevel = prototype.maxLevel instance.storageMax = prototype.storageMax ---@deprecated instance.storageCurrent = prototype.storageCurrent ---@deprecated instance.crit = prototype.crit instance.dodge = prototype.dodge instance.damageBoost = prototype.damageBoost + instance.defenseBoost = prototype.defenseBoost instance.zone = Zone(instance.name) if instance.from and instance.to then @@ -41,19 +43,43 @@ function Hazard:getHazardPlayerAndPoints(damageMap) end if hazardPoints == -1 then - hazardPoints = 1 + hazardPoints = self.minLevel end return hazardPlayer, hazardPoints end +function Hazard:getCurrentLevel(players) + local hazardPlayer = nil + local hazardPoints = -1 + for _, player in ipairs(players) do + local playerHazardPoints = self:getPlayerCurrentLevel(player) + + if playerHazardPoints < hazardPoints or hazardPoints == -1 then + hazardPlayer = player + hazardPoints = playerHazardPoints + end + end + + if hazardPoints == -1 then + hazardPoints = self.minLevel + end + + return hazardPoints +end + function Hazard:getPlayerCurrentLevel(player) if self.storageCurrent then local fromStorage = player:getStorageValue(self.storageCurrent) - return fromStorage <= 0 and 1 or fromStorage + return fromStorage <= 0 and self.minLevel or fromStorage end - local fromKV = player:kv():scoped(self.name):get("currentLevel") or 1 - return fromKV <= 0 and 1 or fromKV + local fromKV = player:kv():scoped(self.name):get("current-level") or self.minLevel + return fromKV <= 0 and self.minLevel or fromKV +end + +function Hazard:getPlayerMaxLevelEver(player) + local fromKV = player:kv():scoped(self.name):get("max-level-set") or self.minLevel + return fromKV <= 0 and self.minLevel or fromKV end function Hazard:setPlayerCurrentLevel(player, level) @@ -64,7 +90,11 @@ function Hazard:setPlayerCurrentLevel(player, level) if self.storageCurrent then player:setStorageValue(self.storageCurrent, level) else - player:kv():scoped(self.name):set("currentLevel", level) + player:kv():scoped(self.name):set("current-level", level) + local maxEver = player:kv():scoped(self.name):get("max-level-set") or self.minLevel + if level > maxEver then + player:kv():scoped(self.name):set("max-level-set", level) + end end local zones = player:getZones() if not zones then @@ -86,11 +116,10 @@ end function Hazard:getPlayerMaxLevel(player) if self.storageMax then local fromStorage = player:getStorageValue(self.storageMax) - return fromStorage <= 0 and 1 or fromStorage + return fromStorage <= 0 and self.minLevel or fromStorage end - local fromKV = player:kv():scoped(self.name):get("maxLevel") or 1 - - return fromKV <= 0 and 1 or fromKV + local fromKV = player:kv():scoped(self.name):get("max-level") or self.minLevel + return fromKV <= 0 and self.minLevel or fromKV end function Hazard:levelUp(player) @@ -110,7 +139,7 @@ function Hazard:setPlayerMaxLevel(player, level) player:setStorageValue(self.storageMax, level) return end - player:kv():scoped(self.name):set("maxLevel", level) + player:kv():scoped(self.name):set("max-level", level) end function Hazard:isInZone(position) @@ -181,6 +210,7 @@ function HazardMonster.onSpawn(monster, position) monster:hazardCrit(hazard.crit) monster:hazardDodge(hazard.dodge) monster:hazardDamageBoost(hazard.damageBoost) + monster:hazardDefenseBoost(hazard.defenseBoost) end end end diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp index 541da679351..a0f602e5ffb 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_definitions.hpp @@ -88,6 +88,7 @@ enum ConfigKey_t : uint16_t { HAZARD_CRITICAL_INTERVAL, HAZARD_CRITICAL_MULTIPLIER, HAZARD_DAMAGE_MULTIPLIER, + HAZARD_DEFENSE_MULTIPLIER, HAZARD_DODGE_MULTIPLIER, HAZARD_EXP_BONUS_MULTIPLIER, HAZARD_LOOT_BONUS_MULTIPLIER, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 97d1052ad03..bb5a3a954a2 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -297,6 +297,7 @@ bool ConfigManager::load() { loadIntConfig(L, HAZARD_CRITICAL_CHANCE, "hazardCriticalChance", 750); loadIntConfig(L, HAZARD_CRITICAL_MULTIPLIER, "hazardCriticalMultiplier", 25); loadIntConfig(L, HAZARD_DAMAGE_MULTIPLIER, "hazardDamageMultiplier", 200); + loadIntConfig(L, HAZARD_DEFENSE_MULTIPLIER, "hazardDefenseMultiplier", 0); loadIntConfig(L, HAZARD_DODGE_MULTIPLIER, "hazardDodgeMultiplier", 85); loadIntConfig(L, HAZARD_PODS_DROP_MULTIPLIER, "hazardPodsDropMultiplier", 87); loadIntConfig(L, HAZARD_PODS_TIME_TO_DAMAGE, "hazardPodsTimeToDamage", 2000); diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index bc77050268d..34fe85cc0f0 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -984,6 +984,9 @@ BlockType_t Creature::blockHit(std::shared_ptr attacker, CombatType_t mitigateDamage(combatType, blockType, damage); + if (damage != 0) { + onTakeDamage(attacker, damage); + } onAttacked(); return blockType; } diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 2052e7a2866..7e2f6595f52 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -449,6 +449,7 @@ class Creature : virtual public Thing, public SharedObject { virtual void onGainExperience(uint64_t gainExp, std::shared_ptr target); virtual void onAttackedCreatureBlockHit(BlockType_t) { } virtual void onBlockHit() { } + virtual void onTakeDamage(std::shared_ptr, int32_t) { } virtual void onChangeZone(ZoneType_t zone); virtual void onAttackedCreatureChangeZone(ZoneType_t zone); virtual void onIdleStatus(); diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index b6194543099..34da8a26682 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -272,6 +272,12 @@ class Monster final : public Creature { void setHazardSystemDamageBoost(bool value) { hazardDamageBoost = value; } + bool getHazardSystemDefenseBoost() const { + return hazardDefenseBoost; + } + void setHazardSystemDefenseBoost(bool value) { + hazardDefenseBoost = value; + } // Hazard end void updateTargetList(); @@ -391,6 +397,7 @@ class Monster final : public Creature { bool hazardCrit = false; bool hazardDodge = false; bool hazardDamageBoost = false; + bool hazardDefenseBoost = false; void onCreatureEnter(std::shared_ptr creature); void onCreatureLeave(std::shared_ptr creature); diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 47789f5ba18..64807ec0f89 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -2482,6 +2482,9 @@ void Player::onBlockHit() { } } +void Player::onTakeDamage(std::shared_ptr attacker, int32_t damage) { +} + void Player::onAttackedCreatureBlockHit(BlockType_t blockType) { lastAttackBlockType = blockType; @@ -7725,6 +7728,17 @@ void Player::parseAttackDealtHazardSystem(CombatDamage &damage, std::shared_ptr< return; } } + if (monster->getHazardSystemDefenseBoost()) { + stage = points * static_cast(g_configManager().getNumber(HAZARD_DEFENSE_MULTIPLIER, __FUNCTION__)); + if (stage != 0) { + damage.exString = "(hazard -" + std::to_string(stage / 100) + "%)"; + damage.primary.value -= static_cast(std::ceil((static_cast(damage.primary.value) * stage) / 10000)); + if (damage.secondary.value != 0) { + damage.secondary.value -= static_cast(std::ceil((static_cast(damage.secondary.value) * stage) / 10000)); + } + return; + } + } } /******************************************************************************* diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index a33c538c814..c717f84c6f4 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -936,6 +936,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void onGainSharedExperience(uint64_t gainExp, std::shared_ptr target); void onAttackedCreatureBlockHit(BlockType_t blockType) override; void onBlockHit() override; + void onTakeDamage(std::shared_ptr attacker, int32_t damage) override; void onChangeZone(ZoneType_t zone) override; void onAttackedCreatureChangeZone(ZoneType_t zone) override; void onIdleStatus() override; diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 5e32115a21c..0c6bcf8c5bf 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -589,7 +589,24 @@ int MonsterFunctions::luaMonsterHazardDamageBoost(lua_State* L) { pushBoolean(L, monster->getHazardSystemDamageBoost()); } else { monster->setHazardSystemDamageBoost(hazardDamageBoost); - pushBoolean(L, monster->getHazardSystemCrit()); + pushBoolean(L, monster->getHazardSystemDamageBoost()); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int MonsterFunctions::luaMonsterHazardDefenseBoost(lua_State* L) { + // get: monster:hazardDefenseBoost() ; set: monster:hazardDefenseBoost(hazardDefenseBoost) + std::shared_ptr monster = getUserdataShared(L, 1); + bool hazardDefenseBoost = getBoolean(L, 2, false); + if (monster) { + if (lua_gettop(L) == 1) { + pushBoolean(L, monster->getHazardSystemDefenseBoost()); + } else { + monster->setHazardSystemDefenseBoost(hazardDefenseBoost); + pushBoolean(L, monster->getHazardSystemDefenseBoost()); } } else { lua_pushnil(L); diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp index 9dbb3f81cc9..a74253a1905 100644 --- a/src/lua/functions/creatures/monster/monster_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_functions.hpp @@ -61,6 +61,7 @@ class MonsterFunctions final : LuaScriptInterface { registerMethod(L, "Monster", "hazardCrit", MonsterFunctions::luaMonsterHazardCrit); registerMethod(L, "Monster", "hazardDodge", MonsterFunctions::luaMonsterHazardDodge); registerMethod(L, "Monster", "hazardDamageBoost", MonsterFunctions::luaMonsterHazardDamageBoost); + registerMethod(L, "Monster", "hazardDefenseBoost", MonsterFunctions::luaMonsterHazardDefenseBoost); CharmFunctions::init(L); LootFunctions::init(L); @@ -120,6 +121,7 @@ class MonsterFunctions final : LuaScriptInterface { static int luaMonsterHazardCrit(lua_State* L); static int luaMonsterHazardDodge(lua_State* L); static int luaMonsterHazardDamageBoost(lua_State* L); + static int luaMonsterHazardDefenseBoost(lua_State* L); friend class CreatureFunctions; };