diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/AI/CoreAI/UnitAI.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/Creature.cpp | 58 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/Creature.h | 7 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 19 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 46 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 4 | ||||
-rw-r--r-- | src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp | 1 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 4 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 3 | ||||
-rw-r--r-- | src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp | 5 |
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 |