Core/Creatures: move level and faction initialization into SummonInfo API

This commit is contained in:
Ovahlord committed 2025-08-17 15:12:10 +02:00
1 parent dd58d5ec0e
commit ec1ab2a5e7
9 files changed
+143 -96

No files matched your search

+29 -9
View File
@@ -596,7 +596,10 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/,
if (!GetCreatureAddon())
SetSheath(SHEATH_STATE_MELEE);
SetFaction(cInfo->faction);
if (SummonInfo const* summonInfo = GetSummonInfo())
SetFaction(summonInfo->GetFactionId().value_or(cInfo->faction));
else
SetFaction(cInfo->faction);
uint32 npcflags = 0, unitFlags = 0, unitFlags2 = 0;
ObjectMgr::ChooseCreatureFlags(cInfo, &npcflags, &unitFlags, &unitFlags2, _staticFlags, data);
@@ -1414,12 +1417,22 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask)
void Creature::SelectLevel()
{
CreatureTemplate const* cInfo = GetCreatureTemplate();
uint8 level = [&]() -> uint8
{
// Some summons override their default level selection
if (SummonInfo const* summonInfo = GetSummonInfo())
if (summonInfo->GetCreatureLevel().has_value())
return *summonInfo->GetCreatureLevel();
CreatureTemplate const* cInfo = GetCreatureTemplate();
// level
uint8 minlevel = std::min(cInfo->maxlevel, cInfo->minlevel);
uint8 maxlevel = std::max(cInfo->maxlevel, cInfo->minlevel);
return minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel);
}();
// level
uint8 minlevel = std::min(cInfo->maxlevel, cInfo->minlevel);
uint8 maxlevel = std::max(cInfo->maxlevel, cInfo->minlevel);
uint8 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel);
SetLevel(level);
}
@@ -1430,10 +1443,17 @@ void Creature::UpdateLevelDependantStats()
CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(getLevel(), cInfo->unit_class);
// health
float healthmod = _GetHealthMod(rank);
uint32 health = [&]() -> uint32
{
// Some summons have their maximum health overridden by their summon spell effect values (e.g. player Shaman totems)
if (SummonInfo const* summonInfo = GetSummonInfo())
if (summonInfo->GetMaxHealth().has_value())
return *_summonInfo->GetMaxHealth();
uint32 basehp = stats->GenerateHealth(cInfo);
uint32 health = uint32(basehp * healthmod);
float healthmod = _GetHealthMod(rank);
uint32 basehp = stats->GenerateHealth(cInfo);
return uint32(basehp * healthmod);
}();
SetCreateHealth(health);
SetMaxHealth(health);
@@ -19,20 +19,21 @@
#include "Creature.h"
#include "DBCStores.h"
#include "DBCEnums.h"
#include "Log.h"
#include "ObjectAccessor.h"
#include "SummonInfoArgs.h"
SummonInfo::SummonInfo(Creature* summonedCreature, SummonInfoArgs const& args) :
_summonedCreature(ASSERT_NOTNULL(summonedCreature)), _summonerGUID(args.SummonerGUID), _remainingDuration(args.Duration),
_maxHealth(args.MaxHealth), _summonSpellId(args.SummonSpellId),_despawnOnSummonerLogout(false),
_despawnOnSummonerDeath(false), _despawnWhenExpired(false)
_summonedCreature(ASSERT_NOTNULL(summonedCreature)), _summonerGUID(args.Summoner ? args.Summoner->GetGUID() : ObjectGuid::Empty),
_remainingDuration(args.Duration), _maxHealth(args.MaxHealth), _creatureLevel(args.CreatureLevel),
_flags(SummonPropertiesFlags::None), _control(SummonPropertiesControl::None), _summonSlot(SummonPropertiesSlot::None)
{
if (args.SummonPropertiesId.has_value())
InitializeSummonProperties(*args.SummonPropertiesId);
InitializeSummonProperties(*args.SummonPropertiesId, Object::ToUnit(args.Summoner));
}
void SummonInfo::InitializeSummonProperties(uint32 summonPropertiesId)
void SummonInfo::InitializeSummonProperties(uint32 summonPropertiesId, Unit const* summoner)
{
SummonPropertiesEntry const* summonProperties = sSummonPropertiesStore.LookupEntry(summonPropertiesId);
if (!summonProperties)
@@ -44,12 +45,27 @@ void SummonInfo::InitializeSummonProperties(uint32 summonPropertiesId)
if (summonProperties->Faction)
_factionId = summonProperties->Faction;
_despawnOnSummonerLogout = summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::DespawnOnSummonerLogout);
_despawnOnSummonerDeath = summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::DespawnOnSummonerDeath);
_despawnWhenExpired = summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::DespawnWhenExpired);
_flags = summonProperties->GetFlags();
_summonSlot = static_cast<SummonPropertiesSlot>(summonProperties->Slot);
_control = static_cast<SummonPropertiesControl>(summonProperties->Control);
//if (_despawnOnSummonerDeath && summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::DontDespawnOnSummonerDeath))
// _despawnOnSummonerDeath = false;
if (summoner)
{
if (_flags.HasFlag(SummonPropertiesFlags::UseSummonerFaction))
_factionId = summoner->GetFaction();
if (_control != SummonPropertiesControl::None)
{
// Controlled summons inherit the level of their summoner unless explicitly stated otherwise.
// Level can be overridden either by SummonPropertiesFlags::UseCreatureLevel or by a spell effect value
if (!_flags.HasFlag(SummonPropertiesFlags::UseCreatureLevel) && !_creatureLevel.has_value())
_creatureLevel = summoner->getLevel();
// Controlled summons inherit their summoner's faction if not overridden by DBC data
if (!_factionId.has_value())
_factionId = summoner->GetFaction();
}
}
}
Creature* SummonInfo::GetSummonedCreature() const
@@ -59,18 +75,12 @@ Creature* SummonInfo::GetSummonedCreature() const
Unit* SummonInfo::GetUnitSummoner() const
{
if (!_summonerGUID.has_value())
return nullptr;
return ObjectAccessor::GetUnit(*_summonedCreature, *_summonerGUID);
return ObjectAccessor::GetUnit(*_summonedCreature, _summonerGUID);
}
GameObject* SummonInfo::GetGameObjectSummoner() const
{
if (!_summonerGUID.has_value())
return nullptr;
return ObjectAccessor::GetGameObject(*_summonedCreature, *_summonerGUID);
return ObjectAccessor::GetGameObject(*_summonedCreature, _summonerGUID);
}
Optional<Milliseconds> SummonInfo::GetRemainingDuration() const
@@ -83,42 +93,68 @@ Optional<uint64> SummonInfo::GetMaxHealth() const
return _maxHealth;
}
Optional<uint32> SummonInfo::GetSummonSpellId() const
{
return _summonSpellId;
}
Optional<uint32> SummonInfo::GetFactionId() const
{
return _factionId;
}
Optional<uint8> SummonInfo::GetCreatureLevel() const
{
return _creatureLevel;
}
bool SummonInfo::DespawnsOnSummonerLogout() const
{
return _despawnOnSummonerLogout;
return _flags.HasFlag(SummonPropertiesFlags::DespawnOnSummonerLogout);
}
void SummonInfo::SetDespawnOnSummonerLogout(bool set)
{
_despawnOnSummonerLogout = set;
if (set)
_flags |= SummonPropertiesFlags::DespawnOnSummonerLogout;
else
_flags.RemoveFlag(SummonPropertiesFlags::DespawnOnSummonerLogout);
}
bool SummonInfo::DespawnsOnSummonerDeath() const
{
return _despawnOnSummonerDeath;
return _flags.HasFlag(SummonPropertiesFlags::DespawnOnSummonerDeath);
}
void SummonInfo::SetDespawnOnSummonerDeath(bool set)
{
_despawnOnSummonerDeath = set;
if (set)
_flags |= SummonPropertiesFlags::DespawnOnSummonerDeath;
else
_flags.RemoveFlag(SummonPropertiesFlags::DespawnOnSummonerDeath);
}
bool SummonInfo::DespawnsWhenExpired() const
{
return _despawnWhenExpired;
return _flags.HasFlag(SummonPropertiesFlags::DespawnWhenExpired);
}
void SummonInfo::SetDespawnWhenExpired(bool set)
{
_despawnWhenExpired = set;
if (set)
_flags |= SummonPropertiesFlags::DespawnWhenExpired;
else
_flags.RemoveFlag(SummonPropertiesFlags::DespawnWhenExpired);
}
bool SummonInfo::UsesSummonerFaction() const
{
return _flags.HasFlag(SummonPropertiesFlags::UseSummonerFaction);
}
void SummonInfo::SetUseSummonerFaction(bool set)
{
if (set)
_flags |= SummonPropertiesFlags::UseSummonerFaction;
else
_flags.RemoveFlag(SummonPropertiesFlags::UseSummonerFaction);
}
bool SummonInfo::IsControlledBySummoner() const
{
return _control > SummonPropertiesControl::None;
}
@@ -18,8 +18,10 @@
#ifndef _SummonInfo_h__
#define _SummonInfo_h__
#include "DBCEnums.h"
#include "Define.h"
#include "Duration.h"
#include "EnumFlag.h"
#include "ObjectGuid.h"
#include "Optional.h"
@@ -40,10 +42,10 @@ public:
explicit SummonInfo(Creature* summonedCreature, SummonInfoArgs const& args);
// Initializes additional settings based on the provided SummonProperties ID.
void InitializeSummonProperties(uint32 summonPropertiesId);
void InitializeSummonProperties(uint32 summonPropertiesId, Unit const* summoner);
// Returns the creature that is tied to this SummonInfo instance.
Creature* GetSummonedCreature() const;
// Returns the Unit summoner, or nullptr if no summoner has been provided or if the summoner is not a Unit.
// Returns the Unit summoner, or nullptr if no summoner has been provided or if the summoner is not an Unit.
Unit* GetUnitSummoner() const;
// Returns the GameObject summoner, or nullptr if no summoner has been provided or if the summoner is not a GameObject.
GameObject* GetGameObjectSummoner() const;
@@ -52,10 +54,10 @@ public:
Optional<Milliseconds> GetRemainingDuration() const;
// Returns the health amount that will override the default max health calculation. Nullopt when no amount is provided.
Optional<uint64> GetMaxHealth() const;
// Returns the ID of the spell that has been used to summon the creature. Nullopt when the creature has not been summoned by a spell (scripted summons/ other APIs).
Optional<uint32> GetSummonSpellId() const;
// Returns the FactionTemplate ID of the summon that is overriding the default ID of the creature. Nullopt when the faction has not been overriden.
Optional<uint32> GetFactionId() const;
// Returns the level of the creature that will override the default level calculation. Nullopt when the creature uses its default values.
Optional<uint8> GetCreatureLevel() const;
// Returns true when the summon will despawn when the summoner logs out. This also includes despawning and teleporting between map instances.
bool DespawnsOnSummonerLogout() const;
@@ -69,17 +71,23 @@ public:
bool DespawnsWhenExpired() const;
// Marks the summon to despawn after its duration has expired. If disabled, the summon will just die.
void SetDespawnWhenExpired(bool set);
// Returns true when the summon will inherit its summoner's faction.
bool UsesSummonerFaction() const;
// Marks the summon to inherit its summoner's faction.
void SetUseSummonerFaction(bool set);
// Returns true when the summon is either a Guardian, Pet, Vehicle or Possessed summon
bool IsControlledBySummoner() const;
private:
Creature* _summonedCreature;
Optional<ObjectGuid> _summonerGUID;
ObjectGuid _summonerGUID;
Optional<Milliseconds> _remainingDuration; // NYI
Optional<uint64> _maxHealth; // NYI
Optional<uint32> _summonSpellId; // NYI
Optional<uint32> _factionId; // NYI
bool _despawnOnSummonerLogout; // NYI
bool _despawnOnSummonerDeath; // NYI
bool _despawnWhenExpired; // NYI
Optional<uint64> _maxHealth; // Implemented in Creature::UpdateLevelDependantStats
Optional<uint32> _factionId; // Implemented in Creature::UpdateEntry
Optional<uint8> _creatureLevel; // Implemented in Creature::SelectLevel
EnumFlag<SummonPropertiesFlags> _flags;
SummonPropertiesControl _control;
SummonPropertiesSlot _summonSlot;
};
#endif // _SummonInfo_h__
@@ -21,16 +21,17 @@
#include "Define.h"
#include "Duration.h"
#include "Optional.h"
#include "ObjectGuid.h"
class WorldObject;
// Controls the behavior of a summoned creature and must be set with extreme care. If you want a blank summon that just exists as a permanent spawn, leave all fields untouched.
struct SummonInfoArgs
{
Optional<ObjectGuid> SummonerGUID;
WorldObject const* Summoner = nullptr;
Optional<uint64> MaxHealth;
Optional<Milliseconds> Duration;
Optional<uint32> SummonPropertiesId;
Optional<uint32> SummonSpellId;
Optional<uint8> CreatureLevel;
};
#endif // _SummonInfoArgs_h__
@@ -172,8 +172,6 @@ void TempSummon::InitStats(uint32 duration)
if (owner && IsTrigger() && m_spells[0])
{
SetFaction(owner->GetFaction());
SetLevel(owner->getLevel());
if (owner->GetTypeId() == TYPEID_PLAYER)
m_ControlledByPlayer = true;
}
@@ -197,9 +195,6 @@ void TempSummon::InitStats(uint32 duration)
if (m_Properties->Control != SUMMON_CATEGORY_WILD)
{
if (!m_Properties->Faction)
SetFaction(owner->GetFaction());
// Creator guid is always set for allied summons
SetCreatorGUID(owner->GetGUID());
@@ -218,10 +213,6 @@ void TempSummon::InitStats(uint32 duration)
if (owner->IsTotem())
owner->m_Controlled.insert(this);
}
// If property has a faction defined, use it.
if (m_Properties->Faction)
SetFaction(m_Properties->Faction);
}
void TempSummon::InitSummon()
@@ -393,7 +384,7 @@ void Guardian::InitStats(uint32 duration)
{
Minion::InitStats(duration);
InitStatsForLevel(GetOwner()->getLevel());
InitStatsForLevel(getLevel());
if (GetOwner()->GetTypeId() == TYPEID_PLAYER && HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
m_charmInfo->InitCharmCreateSpells();
@@ -423,7 +414,6 @@ Puppet::Puppet(SummonPropertiesEntry const* properties, Unit* owner)
void Puppet::InitStats(uint32 duration)
{
Minion::InitStats(duration);
SetLevel(GetOwner()->getLevel());
SetReactState(REACT_PASSIVE);
}
+17 -28
View File
@@ -2076,6 +2076,22 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatur
break;
}
// Store special settings which have been extracted from spell effects/scripts
SummonInfoArgs args;
if (summonArgs.Summoner)
args.Summoner = summonArgs.Summoner;
if (summonArgs.SummonProperties)
args.SummonPropertiesId = summonArgs.SummonProperties->ID;
if (summonArgs.SummonDuration)
args.Duration = Milliseconds(summonArgs.SummonDuration);
if (summonArgs.CreatureLevel > 0)
args.CreatureLevel = summonArgs.CreatureLevel;
if (summonArgs.SummonHealth)
args.MaxHealth = summonArgs.SummonHealth;
// Initialize the SummonInfo API which marks the creature as Summon
summon->InitializeSummonInfo(args);
// Create creature entity
if (!summon->Create(GenerateLowGuid<HighGuid::Unit>(), this, entry, pos, nullptr, summonArgs.VehicleRecID, true))
{
@@ -2087,21 +2103,7 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatur
if (summonArgs.Summoner)
PhasingHandler::InheritPhaseShift(summon, summonArgs.Summoner);
// Store special settings which have been extracted from spell effects/scripts
SummonInfoArgs args;
if (summonArgs.Summoner)
args.SummonerGUID = summonArgs.Summoner->GetGUID();
if (summonArgs.SummonProperties)
args.SummonPropertiesId = summonArgs.SummonProperties->ID;
if (summonArgs.SummonDuration)
args.Duration = Milliseconds(summonArgs.SummonDuration);
if (summonArgs.SummonSpellId)
args.SummonSpellId = summonArgs.SummonSpellId;
if (summonArgs.SummonHealth)
args.MaxHealth = summonArgs.SummonHealth;
// Initialize the SummonInfo API which marks the creature as Summon
summon->InitializeSummonInfo(args);
summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, summonArgs.SummonSpellId);
TransportBase* transport = summonArgs.Summoner ? summonArgs.Summoner->GetTransport() : nullptr;
if (transport)
@@ -2115,23 +2117,10 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatur
transport->AddPassenger(summon);
}
// Initialize tempsummon fields
summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, summonArgs.SummonSpellId);
summon->SetHomePosition(pos);
summon->InitStats(summonArgs.SummonDuration);
summon->SetPrivateObjectOwner(summonArgs.PrivateObjectOwner);
// Handle health argument
if (summonArgs.SummonHealth)
{
summon->SetMaxHealth(summonArgs.SummonHealth);
summon->SetHealth(summonArgs.SummonHealth);
}
// Handle creature level argument
if (summonArgs.CreatureLevel)
summon->SetLevel(summonArgs.CreatureLevel);
if (!AddToMap(summon->ToCreature()))
{
// Returning false will cause the object to be deleted - remove from transport
+6 -2
View File
@@ -41,11 +41,15 @@ constexpr float PET_XP_FACTOR = 0.05f;
Pet::Pet(Player* owner, PetType type) :
Guardian(nullptr, owner, true), m_usedTalentCount(0), m_removed(false),
m_petType(type), m_duration(0), m_auraRaidUpdateMask(0), m_loading(false),
m_declinedname(nullptr)
m_declinedname(nullptr), m_petSlot(0)
{
ASSERT(GetOwner());
InitializeSummonInfo({ .SummonerGUID = GetOwner()->GetGUID()});
InitializeSummonInfo(
{
.Summoner = GetOwner(),
.CreatureLevel = GetOwner()->getLevel()
});
m_unitTypeMask |= UNIT_MASK_PET;
if (type == HUNTER_PET)
+1 -1
View File
@@ -144,7 +144,7 @@ inline bool CompareRespawnInfo::operator()(RespawnInfo const* a, RespawnInfo con
struct TC_GAME_API SummonCreatureExtraArgs
{
public:
SummonCreatureExtraArgs() { }
SummonCreatureExtraArgs() = default;
SummonCreatureExtraArgs& SetSummonDuration(uint32 duration) { SummonDuration = duration; return *this; }
+1 -2
View File
@@ -2010,7 +2010,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
SummonCreatureExtraArgs extraArgs;
extraArgs.SummonProperties = properties;
extraArgs.SummonDuration = duration;
extraArgs.Summoner = caster->IsUnit() ? caster->ToUnit() : nullptr;
extraArgs.Summoner = Object::ToUnit(caster);
extraArgs.SummonSpellId = m_spellInfo->Id;
extraArgs.PrivateObjectOwner = privateObjectOwner;
@@ -2077,7 +2077,6 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
caster->CastSpell(summon, VEHICLE_SPELL_RIDE_HARDCODED, CastSpellExtraArgs(true).AddSpellBP0(extraArgs.SeatNumber));
else if (extraArgs.RideSpell)
caster->CastSpell(summon, extraArgs.RideSpell, true);
}
}
}