Implemented creature sparring system

Big thanks:
@Ovahlord - original script
@ReyDonovan - port
This commit is contained in:
Kulyadev
2022-01-20 14:39:28 +03:00
parent 2212411999
commit b197d4d51a
8 changed files with 150 additions and 1 deletions

View File

@@ -0,0 +1,13 @@
DROP TABLE IF EXISTS `creature_sparring_template`;
CREATE TABLE `creature_sparring_template` (
`creature_id` mediumint(8) unsigned NOT NULL,
`health_limit_pct` float DEFAULT "0",
`comment` varchar(255) DEFAULT "",
PRIMARY KEY (`creature_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DELETE FROM `creature_sparring_template` WHERE `creature_id` IN (49422, 49423, 49428);
INSERT INTO `creature_sparring_template`(`creature_id`, `health_limit_pct`, `comment`) VALUES
(49422,85,"Rotbrain Berserker"),
(49423,85,"Rotbrain Magus"),
(49428,85,"Deathguard Protector");

View File

@@ -2947,3 +2947,8 @@ void Creature::RecalculateDynamicHealth(uint32 newHealth)
SetHealth(newHealth - damage);
}
}
float Creature::GetSparringHealthLimit() const
{
return sObjectMgr->GetSparringHealthLimitFor(GetEntry());
}

View File

@@ -565,6 +565,8 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
CreatureData const* GetCreatureData() const { return m_creatureData; }
CreatureAddon const* GetCreatureAddon() const;
float GetSparringHealthLimit() const;
std::string GetAIName() const;
std::string GetScriptName() const;
uint32 GetScriptId() const;
@@ -832,4 +834,6 @@ class ForcedDespawnDelayEvent : public BasicEvent
Creature& m_owner;
};
typedef std::unordered_map<uint32, float /*SparringHealthLimit*/> CreatureSparringTemplateMap;
#endif

View File

@@ -783,6 +783,22 @@ void Unit::DealDamageMods(Unit* victim, uint32 &damage, uint32* absorb)
uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss)
{
if (Creature* target = victim->ToCreature())
{
if (GetTypeId() == TYPEID_UNIT && !IsCharmedOwnedByPlayerOrPlayer())
{
float sparringLimitPct = target->GetSparringHealthLimit();
if (sparringLimitPct != 0.0f)
{
if (damage >= target->GetHealth()) // First check: if we have a sparring limit we will never allow creatures to kill the sparring victim
damage = target->GetHealth() - 1;
else if (target->GetHealthPct() <= sparringLimitPct) // Second check: stop incomming damage when we have surpassed the health limit
damage = 0;
}
}
}
if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->GetSession()->GetSecurity() < SEC_GAMEMASTER && damage >= sWorld->getIntConfig(CONFIG_DAMAGE_LOG_MIN_DAMAGE))
{
float takenMod = victim->GetTotalAuraModifier(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
@@ -2221,6 +2237,22 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe
uint32 splitAbsorb = dmgInfo.GetAbsorbedSplit();
DealDamageMods(caster, splitted, &splitAbsorb);
if (Creature* target = dmgInfo.GetVictim()->ToCreature())
{
if (GetTypeId() == TypeID::TYPEID_UNIT && !IsCharmedOwnedByPlayerOrPlayer())
{
float sparringLimitPct = target->GetSparringHealthLimit();
if (sparringLimitPct != 0.0f)
{
if (target->GetHealthPct() <= sparringLimitPct)
{
dmgInfo.ModifyDamage(dmgInfo.GetDamage() * -1);
}
}
}
}
SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitted, schoolMask, splitAbsorb, 0, false, 0, false);
CleanDamage cleanDamage = CleanDamage(splitted, 0, BASE_ATTACK, MELEE_HIT_NORMAL);
@@ -2359,6 +2391,23 @@ void Unit::AttackerStateUpdate(Unit* victim, bool ignoreLos, WeaponAttackType at
CalculateMeleeDamage(victim, 0, &damageInfo, attType);
// Send log damage message to client
DealDamageMods(victim, damageInfo.damage, &damageInfo.absorb);
if (Creature* target = victim->ToCreature())
{
if (GetTypeId() == TypeID::TYPEID_UNIT && !IsCharmedOwnedByPlayerOrPlayer())
{
float sparringLimitPct = target->GetSparringHealthLimit();
if (sparringLimitPct != 0.0f)
{
if (target->GetHealthPct() <= sparringLimitPct)
{
damageInfo.HitInfo |= HITINFO_FAKE_DAMAGE;
}
}
}
}
SendAttackStateUpdate(&damageInfo);
DealMeleeDamage(&damageInfo, true);

View File

@@ -704,6 +704,51 @@ void ObjectMgr::LoadCreatureDifficultyModifiers()
TC_LOG_INFO("server.loading", ">> Loaded %u creature difficulty modifiers in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
void ObjectMgr::LoadCreatureSparringTemplate()
{
uint32 oldMSTime = getMSTime();
// 0 1
QueryResult result = WorldDatabase.Query("SELECT creature_id, health_limit_pct 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();
float healthPct = 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_sparring_template`", entry);
continue;
}
if (healthPct > 100.0f)
{
TC_LOG_ERROR("sql.sql", "Sparring entry (Entry: %u) exceeds the health percentage limit. Setting to 100.", entry);
healthPct = 100.0f;
}
if (healthPct <= 0.0f)
{
TC_LOG_ERROR("sql.sql", "Sparring entry (Entry: %u) has a negative or too small health percentage. Setting to 0.1.", entry);
healthPct = 0.1f;
}
_creatureSparringTemplateStore[entry] = 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)

View File

@@ -1108,6 +1108,7 @@ class ObjectMgr
void LoadGraveyardOrientations();
void LoadCreatureTemplates();
void LoadCreatureTemplateAddons();
void LoadCreatureSparringTemplate();
void LoadCreatureDifficultyModifiers();
void CheckCreatureTemplate(CreatureTemplate const* cInfo);
void LoadTempSummons();
@@ -1378,8 +1379,20 @@ class ObjectMgr
return &itr->second;
}
GameObjectData& NewGOData(uint32 guid) { return _gameObjectDataStore[guid]; }
void DeleteGOData(uint32 guid);
float GetSparringHealthLimitFor(uint32 entry) const
{
auto itr = _creatureSparringTemplateStore.find(entry);
if (itr != _creatureSparringTemplateStore.end())
{
return itr->second;
}
return 0.0f;
}
TrinityStringLocale const* GetTrinityStringLocale(int32 entry) const
{
TrinityStringLocaleContainer::const_iterator itr = _trinityStringLocaleStore.find(entry);
@@ -1792,6 +1805,7 @@ class ObjectMgr
CreatureAddonContainer _creatureAddonStore;
GameObjectAddonContainer _gameObjectAddonStore;
CreatureAddonContainer _creatureTemplateAddonStore;
CreatureSparringTemplateMap _creatureSparringTemplateStore;
EquipmentInfoContainer _equipmentInfoStore;
LinkedRespawnContainer _linkedRespawnStore;
CreatureLocaleContainer _creatureLocaleStore;

View File

@@ -2811,6 +2811,22 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
caster->CalculateSpellDamageTaken(&damageInfo, m_damage * m_spellValue->Multiplier, m_spellInfo, m_attackType, target->crit, procVictim, createProcExtendMask(&damageInfo, missInfo));
caster->DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb);
if (Creature* target = damageInfo.target->ToCreature())
{
if (caster->GetTypeId() == TYPEID_UNIT && !caster->IsCharmedOwnedByPlayerOrPlayer())
{
float sparringLimitPct = target->GetSparringHealthLimit();
if (sparringLimitPct != 0.0f)
{
if (target->GetHealthPct() <= sparringLimitPct)
{
damageInfo.damage = 0;
}
}
}
}
// Send log damage message to client
caster->SendSpellNonMeleeDamageLog(&damageInfo);
caster->DealSpellDamage(&damageInfo, true);

View File

@@ -568,7 +568,7 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP] = sConfigMgr->GetBoolDefault("AllowTwoSide.Interaction.Group", false);
m_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD] = sConfigMgr->GetBoolDefault("AllowTwoSide.Interaction.Guild", false);
m_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION] = sConfigMgr->GetBoolDefault("AllowTwoSide.Interaction.Auction", false);
m_bool_configs[CONFIG_ALLOW_TWO_SIDE_TRADE] = sConfigMgr->GetBoolDefault("AllowTwoSide.trade", false);
m_bool_configs[CONFIG_ALLOW_TWO_SIDE_TRADE] = sConfigMgr->GetBoolDefault("AllowTwoSide.Trade", false);
m_int_configs[CONFIG_STRICT_PLAYER_NAMES] = sConfigMgr->GetIntDefault ("StrictPlayerNames", 0);
m_int_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfigMgr->GetIntDefault ("StrictCharterNames", 0);
m_int_configs[CONFIG_STRICT_PET_NAMES] = sConfigMgr->GetIntDefault ("StrictPetNames", 0);
@@ -1890,6 +1890,9 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading GameObject Addon Data...");
sObjectMgr->LoadGameObjectAddons(); // must be after LoadGameObjectTemplate() and LoadGameobjects()
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()