From 454c3ce065faf1256bd6bf24cd1dbe700b4ead1a Mon Sep 17 00:00:00 2001 From: Ovalord <1Don7H4v3@m41L.com> Date: Fri, 22 Dec 2017 11:45:48 +0100 Subject: [PATCH] Core/Creatures: added sparring system for creatures --- .../4.3.4/custom_2017_12_22_02_world.sql | 7 +++ .../game/Entities/Creature/Creature.cpp | 21 ++++++++ src/server/game/Entities/Creature/Creature.h | 19 +++++++ src/server/game/Entities/Unit/Unit.cpp | 26 ++++++++++ src/server/game/Globals/ObjectMgr.cpp | 52 +++++++++++++++++++ src/server/game/Globals/ObjectMgr.h | 15 ++++++ src/server/game/Spells/Spell.cpp | 7 +++ src/server/game/World/World.cpp | 3 ++ 8 files changed, 150 insertions(+) create mode 100644 sql/updates/world/4.3.4/custom_2017_12_22_02_world.sql diff --git a/sql/updates/world/4.3.4/custom_2017_12_22_02_world.sql b/sql/updates/world/4.3.4/custom_2017_12_22_02_world.sql new file mode 100644 index 00000000000..eaed70cc977 --- /dev/null +++ b/sql/updates/world/4.3.4/custom_2017_12_22_02_world.sql @@ -0,0 +1,7 @@ +DROP TABLE IF EXISTS `creature_sparring_template`; +CREATE TABLE `creature_sparring_template`( + `AttackerEntry` MEDIUMINT(8) UNSIGNED NOT NULL, + `VictimEntry` MEDIUMINT(8) UNSIGNED NOT NULL, + `HealthLimitPct` FLOAT(5) DEFAULT 0, + PRIMARY KEY (`AttackerEntry`, `VictimEntry`) +); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 237b5852fe1..0f60dda7e28 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2680,6 +2680,27 @@ Unit* Creature::SelectNearestHostileUnitInAggroRange(bool useLOS) const return target; } +CreatureSparring const* Creature::GetSparringData(uint32 attackerEntry, uint32 victimEntry) const +{ + return sObjectMgr->GetCreatureSparringInfo(attackerEntry, victimEntry); +} + +bool Creature::CanSparrWith(Creature* victim) const +{ + if (GetSparringData(GetEntry(), victim->GetEntry())) + return true; + + return false; +} + +float Creature::GetSparringHealthLimitPctFor(Creature* victim) const +{ + if (CreatureSparring const* sparrData = GetSparringData(GetEntry(), victim->GetEntry())) + return sparrData->GetHealthLimitPct(); + + return 0.0f; +} + void Creature::UpdateMovementFlags() { // Do not update movement flags if creature is controlled by a player (charm/vehicle) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 03df8a160e3..93c3b490d36 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -353,6 +353,21 @@ struct CreatureAddon typedef std::unordered_map CreatureAddonContainer; typedef std::unordered_map CreatureAddonTemplateContainer; +// `creature_sparring_template` table +struct CreatureSparring +{ + CreatureSparring(uint32 _victimEntry, float _healthLimitPct) + : victimEntry(_victimEntry), healthLimitPct(_healthLimitPct) { } + + uint32 victimEntry; + float healthLimitPct; + + float GetHealthLimitPct() const { return healthLimitPct; } +}; + +typedef std::vector CreatureSparringList; +typedef std::unordered_map CreatureSparringTemplateMap; + // Vendors struct VendorItem { @@ -603,6 +618,10 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma Unit* SelectNearestTargetInAttackDistance(float dist = 0) const; Unit* SelectNearestHostileUnitInAggroRange(bool useLOS = false) const; + CreatureSparring const* GetSparringData(uint32 attackerEntry, uint32 victimEntry) const; + bool CanSparrWith(Creature* victim) const; + float GetSparringHealthLimitPctFor(Creature* victim) const; + void DoFleeToGetAssistance(); void CallForHelp(float fRadius); void CallAssistance(); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 89462c55ee5..24c5dab51f8 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -675,6 +675,17 @@ void Unit::DealDamageMods(Unit const* victim, uint32 &damage, uint32* absorb) co uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss) { + // Sparring Checks + if (Creature* me = ToCreature()) + if (Creature* target = victim->ToCreature()) + if (me->CanSparrWith(target)) + { + if (damage >= victim->GetHealth()) // safety check to prevent creatures getting accidentally killed when the percentage is small + damage = victim->GetHealth() - 1; + else if (target->GetHealthPct() <= me->GetSparringHealthLimitPctFor(target)) + damage = 0; + } + if (victim->IsAIEnabled) victim->GetAI()->DamageTaken(this, damage); @@ -1871,6 +1882,13 @@ void Unit::CalcAbsorbResist(DamageInfo& damageInfo) uint32 split_absorb = 0; DealDamageMods(caster, splitDamage, &split_absorb); + // Sparring Checks + if (Creature* me = caster->ToCreature()) + if (Creature* target = damageInfo.GetVictim()->ToCreature()) + if (me->CanSparrWith(target)) + if (target->GetHealthPct() <= me->GetSparringHealthLimitPctFor(target)) + damageInfo.ModifyDamage(damageInfo.GetDamage() * -1); + SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitDamage, damageInfo.GetSchoolMask(), split_absorb, 0, false, 0, false); CleanDamage cleanDamage = CleanDamage(splitDamage, 0, BASE_ATTACK, MELEE_HIT_NORMAL); @@ -1977,6 +1995,14 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr CalculateMeleeDamage(victim, 0, &damageInfo, attType); // Send log damage message to client DealDamageMods(victim, damageInfo.damage, &damageInfo.absorb); + + // Sparring Checks + if (Creature* me = ToCreature()) + if (Creature* target = victim->ToCreature()) + if (me->CanSparrWith(target)) + if (target->GetHealthPct() <= me->GetSparringHealthLimitPctFor(target)) + damageInfo.HitInfo |= HITINFO_FAKE_DAMAGE; + SendAttackStateUpdate(&damageInfo); DealMeleeDamage(&damageInfo, true); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 1c482facb97..713dc43f4cb 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -643,6 +643,58 @@ void ObjectMgr::LoadCreatureTemplateAddons() TC_LOG_INFO("server.loading", ">> Loaded %u creature template addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadCreatureSparringTemplate() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT AttackerEntry, VictimEntry, HealthLimitPct FROM creature_sparring_template"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 creature template sparring definitions. DB table `creature_sparring_template` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + uint32 victim = fields[1].GetUInt32(); + float healthPct = fields[2].GetFloat(); + + if (!sObjectMgr->GetCreatureTemplate(entry)) + { + TC_LOG_ERROR("sql.sql", "Creature template (Entry: %u) does not exist but has a record in `creature_sparring_template`", entry); + continue; + } + + if (!sObjectMgr->GetCreatureTemplate(victim)) + { + TC_LOG_ERROR("sql.sql", "Creature template (Entry: %u) does not exist but has a record in `creature_sparring_template`", entry); + continue; + } + + if (healthPct > 100.0f) + { + TC_LOG_ERROR("sql.sql", "Sparring entry (Entry: %u, Victim: %u) exceeds the health percentage limit. Setting to 100.", entry, victim); + healthPct = 100.0f; + } + + if (healthPct <= 0.0f) + { + TC_LOG_ERROR("sql.sql", "Sparring entry (Entry: %u, Victim: %u) has a negative or too small health percentage. Setting to 0.1.", entry, victim); + healthPct = 0.1f; + } + + _creatureSparringTemplateStore[entry].emplace_back(CreatureSparring(victim, healthPct)); + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u creature sparring templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) { if (!cInfo) diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 3459db6eed4..357d0d5cf56 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1004,6 +1004,7 @@ class TC_GAME_API ObjectMgr void LoadGraveyardOrientations(); void LoadCreatureTemplates(); void LoadCreatureTemplateAddons(); + void LoadCreatureSparringTemplate(); void LoadCreatureTemplate(Field* fields); void CheckCreatureTemplate(CreatureTemplate const* cInfo); void LoadGameObjectQuestItems(); @@ -1241,6 +1242,19 @@ class TC_GAME_API ObjectMgr return &itr->second; } + CreatureSparring const* GetCreatureSparringInfo(uint32 attackerEntry, uint32 victimEntry) const + { + auto itr = _creatureSparringTemplateStore.find(attackerEntry); + if (itr == _creatureSparringTemplateStore.end()) + return nullptr; + + for (CreatureSparring const& sparring : itr->second) + if (sparring.victimEntry == victimEntry) + return &sparring; + + return nullptr; + } + TrinityString const* GetTrinityString(uint32 entry) const { TrinityStringContainer::const_iterator itr = _trinityStringStore.find(entry); @@ -1533,6 +1547,7 @@ class TC_GAME_API ObjectMgr CreatureModelContainer _creatureModelStore; CreatureAddonContainer _creatureAddonStore; CreatureAddonTemplateContainer _creatureTemplateAddonStore; + CreatureSparringTemplateMap _creatureSparringTemplateStore; GameObjectAddonContainer _gameObjectAddonStore; GameObjectQuestItemMap _gameObjectQuestItemStore; CreatureQuestItemMap _creatureQuestItemStore; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 34c90f27346..1e7257247be 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2486,6 +2486,13 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit); caster->DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); + // Sparring Checks + if (Creature* me = caster->ToCreature()) + if (Creature* target = damageInfo.target->ToCreature()) + if (me->CanSparrWith(target)) + if (target->GetHealthPct() <= me->GetSparringHealthLimitPctFor(target)) + damageInfo.damage = 0; + // Send log damage message to client caster->SendSpellNonMeleeDamageLog(&damageInfo); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 493ffe9833d..7d1855c3b34 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1719,6 +1719,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Creature Quest Items..."); sObjectMgr->LoadCreatureQuestItems(); + TC_LOG_INFO("server.loading", "Loading Creature Sparring Data..."); + sObjectMgr->LoadCreatureSparringTemplate(); + TC_LOG_INFO("server.loading", "Loading Creature Linked Respawn..."); sObjectMgr->LoadLinkedRespawn(); // must be after LoadCreatures(), LoadGameObjects()