Skip to content

Commit

Permalink
Implemented creature sparring system
Browse files Browse the repository at this point in the history
Big thanks:
@Ovahlord - original script
@ReyDonovan - port
  • Loading branch information
alexkulya committed Jan 20, 2022
1 parent 2212411 commit b197d4d
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 1 deletion.
13 changes: 13 additions & 0 deletions sql/updates/world/2022_01_20_00_world_creature_sparring.sql
Original file line number Diff line number Diff line change
@@ -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");
5 changes: 5 additions & 0 deletions src/server/game/Entities/Creature/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2947,3 +2947,8 @@ void Creature::RecalculateDynamicHealth(uint32 newHealth)
SetHealth(newHealth - damage);
}
}

float Creature::GetSparringHealthLimit() const
{
return sObjectMgr->GetSparringHealthLimitFor(GetEntry());
}
4 changes: 4 additions & 0 deletions src/server/game/Entities/Creature/Creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -832,4 +834,6 @@ class ForcedDespawnDelayEvent : public BasicEvent
Creature& m_owner;
};

typedef std::unordered_map<uint32, float /*SparringHealthLimit*/> CreatureSparringTemplateMap;

#endif
49 changes: 49 additions & 0 deletions src/server/game/Entities/Unit/Unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
45 changes: 45 additions & 0 deletions src/server/game/Globals/ObjectMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions src/server/game/Globals/ObjectMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,7 @@ class ObjectMgr
void LoadGraveyardOrientations();
void LoadCreatureTemplates();
void LoadCreatureTemplateAddons();
void LoadCreatureSparringTemplate();
void LoadCreatureDifficultyModifiers();
void CheckCreatureTemplate(CreatureTemplate const* cInfo);
void LoadTempSummons();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1792,6 +1805,7 @@ class ObjectMgr
CreatureAddonContainer _creatureAddonStore;
GameObjectAddonContainer _gameObjectAddonStore;
CreatureAddonContainer _creatureTemplateAddonStore;
CreatureSparringTemplateMap _creatureSparringTemplateStore;
EquipmentInfoContainer _equipmentInfoStore;
LinkedRespawnContainer _linkedRespawnStore;
CreatureLocaleContainer _creatureLocaleStore;
Expand Down
16 changes: 16 additions & 0 deletions src/server/game/Spells/Spell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 4 additions & 1 deletion src/server/game/World/World.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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()

Expand Down

0 comments on commit b197d4d

Please sign in to comment.