aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorModoX <moardox@gmail.com>2023-04-06 01:01:02 +0200
committerGitHub <noreply@github.com>2023-04-06 01:01:02 +0200
commit0750b7f8455df39a64462636ca296c6f2aa2b048 (patch)
tree935678cc42b5829dff44efababb3133c9df4e27a /src
parent20a1e21cc5dc890d858a4a8dceba16a4fc3caa72 (diff)
Core/Creature: Implemented sparring with max health percent thresholds (#27198)
Co-authored-by: Ovah <dreadkiller@gmx.de>
Diffstat (limited to 'src')
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.h2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp58
-rw-r--r--src/server/game/Entities/Creature/Creature.h7
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp19
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp46
-rw-r--r--src/server/game/Globals/ObjectMgr.h4
-rw-r--r--src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp1
-rw-r--r--src/server/game/Spells/Spell.cpp4
-rw-r--r--src/server/game/World/World.cpp3
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp5
10 files changed, 139 insertions, 10 deletions
diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h
index e7165b69d3f..fed0cae7203 100644
--- a/src/server/game/AI/CoreAI/UnitAI.h
+++ b/src/server/game/AI/CoreAI/UnitAI.h
@@ -160,8 +160,6 @@ class TC_GAME_API UnitAI
SpellCastResult DoCastVictim(uint32 spellId, CastSpellExtraArgs const& args = {});
SpellCastResult DoCastAOE(uint32 spellId, CastSpellExtraArgs const& args = {}) { return DoCast(nullptr, spellId, args); }
- virtual bool ShouldSparWith(Unit const* /*target*/) const { return false; }
-
void DoMeleeAttackIfReady();
bool DoSpellAttackIfReady(uint32 spellId);
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 5210e4be33e..8b8c54bd69d 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -294,7 +294,7 @@ Creature::Creature(bool isWorldObject) : Unit(isWorldObject), MapObject(), m_Pla
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0),
m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0),
m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _lastDamagedTime(0),
- _regenerateHealth(true), _isMissingCanSwimFlagOutOfCombat(false), _gossipMenuId(0)
+ _regenerateHealth(true), _isMissingCanSwimFlagOutOfCombat(false), _gossipMenuId(0), _sparringHealthPct(0)
{
m_regenTimer = CREATURE_REGEN_INTERVAL;
@@ -679,6 +679,7 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/,
InitializeMovementFlags();
LoadCreaturesAddon();
+ LoadCreaturesSparringHealth();
LoadTemplateImmunities();
GetThreatManager().EvaluateSuppressed();
@@ -1119,6 +1120,7 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, Posit
}
LoadCreaturesAddon();
+ LoadCreaturesSparringHealth();
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
m_positionZ += GetHoverOffset();
@@ -1676,6 +1678,52 @@ float Creature::GetSpellDamageMod(int32 Rank) const
}
}
+void Creature::OverrideSparringHealthPct(std::vector<float> const& healthPct)
+{
+ _sparringHealthPct = Trinity::Containers::SelectRandomContainerElement(healthPct);
+}
+
+uint32 Creature::CalculateDamageForSparring(Unit* attacker, uint32 damage)
+{
+ if (GetSparringHealthPct() == 0)
+ return damage;
+
+ if (!attacker->IsCreature() || attacker->IsCharmedOwnedByPlayerOrPlayer() || IsCharmedOwnedByPlayerOrPlayer())
+ return damage;
+
+ if (GetHealthPct() <= GetSparringHealthPct())
+ return 0;
+
+ uint32 sparringHealth = GetMaxHealth() * GetSparringHealthPct() / 100;
+ if (GetHealth() - damage <= sparringHealth)
+ return GetHealth() - sparringHealth;
+
+ if (damage >= GetHealth())
+ return GetHealth() - 1;
+
+ return damage;
+}
+
+bool Creature::ShouldFakeDamageFrom(Unit* attacker)
+{
+ if (!GetSparringHealthPct())
+ return false;
+
+ if (!attacker)
+ return false;
+
+ if (!attacker->IsCreature())
+ return false;
+
+ if (attacker->IsCharmedOwnedByPlayerOrPlayer() || IsCharmedOwnedByPlayerOrPlayer())
+ return false;
+
+ if (GetHealthPct() > GetSparringHealthPct())
+ return false;
+
+ return true;
+}
+
bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, CreatureData const* data /*= nullptr*/, uint32 vehId /*= 0*/)
{
SetZoneScript();
@@ -2198,6 +2246,7 @@ void Creature::setDeathState(DeathState s)
Motion_Initialize();
Unit::setDeathState(ALIVE);
LoadCreaturesAddon();
+ LoadCreaturesSparringHealth();
}
}
@@ -2703,10 +2752,15 @@ bool Creature::LoadCreaturesAddon()
TC_LOG_DEBUG("entities.unit", "Spell: {} added to creature {}", *itr, GetGUID().ToString());
}
}
-
return true;
}
+void Creature::LoadCreaturesSparringHealth()
+{
+ if (std::vector<float> const* templateValues = sObjectMgr->GetCreatureTemplateSparringValues(GetCreatureTemplate()->Entry))
+ _sparringHealthPct = Trinity::Containers::SelectRandomContainerElement(*templateValues);
+}
+
/// Send a message to LocalDefense channel for players opposition team in the zone
void Creature::SendZoneUnderAttackMessage(Player* attacker)
{
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index c77feb358e9..a8d14a0a9b0 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -88,6 +88,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
static Creature* CreateCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true, bool allowDuplicate = false);
bool LoadCreaturesAddon();
+ void LoadCreaturesSparringHealth();
void SelectLevel();
void UpdateLevelDependantStats();
void SelectWildBattlePetLevel();
@@ -393,6 +394,11 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void AtEngage(Unit* target) override;
void AtDisengage() override;
+ void OverrideSparringHealthPct(std::vector<float> const& healthPct);
+ float GetSparringHealthPct() { return _sparringHealthPct; }
+ uint32 CalculateDamageForSparring(Unit* attacker, uint32 damage);
+ bool ShouldFakeDamageFrom(Unit* attacker);
+
bool HasCanSwimFlagOutOfCombat() const
{
return !_isMissingCanSwimFlagOutOfCombat;
@@ -509,6 +515,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
uint32 _gossipMenuId;
Optional<uint32> _trainerId;
+ float _sparringHealthPct;
};
class TC_GAME_API AssistDelayEvent : public BasicEvent
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index e3d661a54d2..dda5fb90848 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -752,6 +752,10 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons
{
uint32 tmpDamage = damageTaken;
+ // sparring
+ if (Creature* victimCreature = victim->ToCreature())
+ tmpDamage = victimCreature->CalculateDamageForSparring(attacker, tmpDamage);
+
if (UnitAI* victimAI = victim->GetAI())
victimAI->DamageTaken(attacker, tmpDamage, damagetype, spellProto);
@@ -1969,6 +1973,13 @@ void Unit::HandleEmoteCommand(Emote emoteId, Player* target /*=nullptr*/, Trinit
uint32 split_absorb = 0;
Unit::DealDamageMods(damageInfo.GetAttacker(), caster, splitDamage, &split_absorb);
+ // sparring
+ if (Creature* victimCreature = damageInfo.GetVictim()->ToCreature())
+ {
+ if (victimCreature->ShouldFakeDamageFrom(damageInfo.GetAttacker()))
+ damageInfo.ModifyDamage(damageInfo.GetDamage() * -1);
+ }
+
SpellNonMeleeDamage log(damageInfo.GetAttacker(), caster, (*itr)->GetSpellInfo(), (*itr)->GetBase()->GetSpellVisual(), damageInfo.GetSchoolMask(), (*itr)->GetBase()->GetCastId());
CleanDamage cleanDamage = CleanDamage(splitDamage, 0, BASE_ATTACK, MELEE_HIT_NORMAL);
Unit::DealDamage(damageInfo.GetAttacker(), caster, splitDamage, &cleanDamage, DIRECT_DAMAGE, damageInfo.GetSchoolMask(), (*itr)->GetSpellInfo(), false);
@@ -2133,6 +2144,14 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr
CalculateMeleeDamage(victim, &damageInfo, attType);
// Send log damage message to client
Unit::DealDamageMods(damageInfo.Attacker, victim, damageInfo.Damage, &damageInfo.Absorb);
+
+ // sparring
+ if (Creature* victimCreature = victim->ToCreature())
+ {
+ if (victimCreature->ShouldFakeDamageFrom(damageInfo.Attacker))
+ damageInfo.HitInfo |= HITINFO_FAKE_DAMAGE;
+ }
+
SendAttackStateUpdate(&damageInfo);
_lastDamagedTargetGuid = victim->GetGUID();
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index f75365fe390..b86569af305 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -892,6 +892,47 @@ void ObjectMgr::LoadCreatureTemplateAddons()
TC_LOG_INFO("server.loading", ">> Loaded {} creature template addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
}
+void ObjectMgr::LoadCreatureTemplateSparring()
+{
+ uint32 oldMSTime = getMSTime();
+
+ // 0 1
+ QueryResult result = WorldDatabase.Query("SELECT Entry, NoNPCDamageBelowHealthPct FROM creature_template_sparring");
+
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 creature template sparring definitions. DB table `creature_template_sparring` is empty.");
+ return;
+ }
+
+ uint32 count = 0;
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 entry = fields[0].GetUInt32();
+ float noNPCDamageBelowHealthPct = fields[1].GetFloat();
+
+ if (!sObjectMgr->GetCreatureTemplate(entry))
+ {
+ TC_LOG_ERROR("sql.sql", "Creature template (Entry: %u) does not exist but has a record in `creature_template_sparring`", entry);
+ continue;
+ }
+
+ if (noNPCDamageBelowHealthPct <= 0 || noNPCDamageBelowHealthPct > 100)
+ {
+ TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid NoNPCDamageBelowHealthPct (%f) defined in `creature_template_sparring`. Skipping",
+ entry, noNPCDamageBelowHealthPct);
+ continue;
+ }
+ _creatureTemplateSparringStore[entry].push_back(noNPCDamageBelowHealthPct);
+
+ ++count;
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u creature template sparring rows in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+}
+
void ObjectMgr::LoadCreatureScalingData()
{
uint32 oldMSTime = getMSTime();
@@ -1569,6 +1610,11 @@ CreatureAddon const* ObjectMgr::GetCreatureTemplateAddon(uint32 entry) const
return nullptr;
}
+std::vector<float> const* ObjectMgr::GetCreatureTemplateSparringValues(uint32 entry) const
+{
+ return Trinity::Containers::MapGetValuePtr(_creatureTemplateSparringStore, entry);
+}
+
CreatureMovementData const* ObjectMgr::GetCreatureMovementOverride(ObjectGuid::LowType spawnId) const
{
return Trinity::Containers::MapGetValuePtr(_creatureMovementOverrides, spawnId);
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index cb81f9903ad..e0123294f66 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -499,6 +499,7 @@ struct TrinityString
typedef std::map<ObjectGuid, ObjectGuid> LinkedRespawnContainer;
typedef std::unordered_map<uint32, CreatureTemplate> CreatureTemplateContainer;
typedef std::unordered_map<uint32, CreatureAddon> CreatureTemplateAddonContainer;
+typedef std::unordered_map<uint32, std::vector<float>> CreatureTemplateSparringContainer;
typedef std::unordered_map<ObjectGuid::LowType, CreatureData> CreatureDataContainer;
typedef std::unordered_map<ObjectGuid::LowType, CreatureAddon> CreatureAddonContainer;
typedef std::unordered_map<uint16, CreatureBaseStats> CreatureBaseStatsContainer;
@@ -1159,6 +1160,7 @@ class TC_GAME_API ObjectMgr
GameObjectTemplateAddon const* GetGameObjectTemplateAddon(uint32 entry) const;
GameObjectOverride const* GetGameObjectOverride(ObjectGuid::LowType spawnId) const;
CreatureAddon const* GetCreatureTemplateAddon(uint32 entry) const;
+ std::vector<float> const* GetCreatureTemplateSparringValues(uint32 entry) const;
CreatureMovementData const* GetCreatureMovementOverride(ObjectGuid::LowType spawnId) const;
ItemTemplate const* GetItemTemplate(uint32 entry) const;
ItemTemplateContainer const& GetItemTemplateStore() const { return _itemTemplateStore; }
@@ -1325,6 +1327,7 @@ class TC_GAME_API ObjectMgr
void LoadCreatureLocales();
void LoadCreatureTemplates();
void LoadCreatureTemplateAddons();
+ void LoadCreatureTemplateSparring();
void LoadCreatureTemplate(Field* fields);
void LoadCreatureTemplateGossip();
void LoadCreatureTemplateResistances();
@@ -1920,6 +1923,7 @@ class TC_GAME_API ObjectMgr
std::unordered_map<uint32, CreatureSummonedData> _creatureSummonedDataStore;
CreatureAddonContainer _creatureAddonStore;
CreatureTemplateAddonContainer _creatureTemplateAddonStore;
+ CreatureTemplateSparringContainer _creatureTemplateSparringStore;
std::unordered_map<ObjectGuid::LowType, CreatureMovementData> _creatureMovementOverrides;
GameObjectAddonContainer _gameObjectAddonStore;
GameObjectQuestItemMap _gameObjectQuestItemStore;
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
index 90f8be25ce0..a5d2067d818 100644
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
@@ -149,6 +149,7 @@ void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner, bool active, b
owner->SetSpawnHealth();
owner->LoadCreaturesAddon();
+ owner->LoadCreaturesSparringHealth();
if (owner->IsVehicle())
owner->GetVehicleKit()->Reset(true);
if (CreatureAI* ai = owner->AI())
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 35fa7d853f6..d903c5c9e45 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -2786,7 +2786,9 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell)
hitMask |= createProcHitMask(&damageInfo, MissCondition);
procVictim |= PROC_FLAG_TAKE_ANY_DAMAGE;
- spell->m_damage = damageInfo.damage;
+ // sparring
+ if (Creature* victimCreature = damageInfo.target->ToCreature())
+ damageInfo.damage = victimCreature->CalculateDamageForSparring(damageInfo.attacker, damageInfo.damage);
caster->DealSpellDamage(&damageInfo, true);
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 0f4cde86499..fa79a585ad8 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1980,6 +1980,9 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading Creature template addons...");
sObjectMgr->LoadCreatureTemplateAddons();
+ TC_LOG_INFO("server.loading", "Loading Creature template sparring...");
+ sObjectMgr->LoadCreatureTemplateSparring();
+
TC_LOG_INFO("server.loading", "Loading Creature template scaling...");
sObjectMgr->LoadCreatureScalingData();
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp
index b6809ef5f08..f4d3db39bf7 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp
@@ -1151,11 +1151,6 @@ class npc_thorim_pre_phase : public CreatureScript
thorim->AI()->DoAction(ACTION_INCREASE_PREADDS_COUNT);
}
- bool ShouldSparWith(Unit const* target) const override
- {
- return !target->GetAffectingPlayer();
- }
-
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
// nullify spell damage