mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Core/Creatures: Implement StringId for Creatures, a custom identifier to make finding specific creatures in script easier (#28500)
Allows targeting specific spawns without hardcoding guids or find a bunch of different creatures with a single search Co-authored-by: Shauren <shauren.trinity@gmail.com>
This commit is contained in:
8
sql/updates/world/master/2022_12_27_01_world.sql
Normal file
8
sql/updates/world/master/2022_12_27_01_world.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
--
|
||||
DELETE FROM `trinity_string` WHERE `entry`=5089;
|
||||
INSERT INTO `trinity_string` (`entry`,`content_default`) VALUES
|
||||
(5089,'Template StringID: %.*s\Spawn StringID: %.*s\nScript StringID: %.*s');
|
||||
|
||||
ALTER TABLE `creature` ADD `StringId` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER ScriptName;
|
||||
|
||||
ALTER TABLE `creature_template` ADD `StringId` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER ScriptName;
|
||||
@@ -77,7 +77,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, help FROM command", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, gossip_menu_id, minlevel, maxlevel, HealthScalingExpansion, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, gossip_menu_id, minlevel, maxlevel, HealthScalingExpansion, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName, StringId FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH);
|
||||
|
||||
@@ -391,6 +391,11 @@ inline Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry,
|
||||
return source->FindNearestCreature(entry, maxSearchRange, alive);
|
||||
}
|
||||
|
||||
inline Creature* GetClosestCreatureWithOptions(WorldObject* source, float maxSearchRange, FindCreatureOptions const& options)
|
||||
{
|
||||
return source->FindNearestCreatureWithOptions(maxSearchRange, options);
|
||||
}
|
||||
|
||||
inline GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool spawnedOnly = true)
|
||||
{
|
||||
return source->FindNearestGameObject(entry, maxSearchRange, spawnedOnly);
|
||||
@@ -402,6 +407,12 @@ inline void GetCreatureListWithEntryInGrid(Container& container, WorldObject* so
|
||||
source->GetCreatureListWithEntryInGrid(container, entry, maxSearchRange);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline void GetCreatureListWithOptionsInGrid(Container& container, WorldObject* source, float maxSearchRange, FindCreatureOptions const& options)
|
||||
{
|
||||
source->GetCreatureListWithOptionsInGrid(container, maxSearchRange, options);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline void GetGameObjectListWithEntryInGrid(Container& container, WorldObject* source, uint32 entry, float maxSearchRange)
|
||||
{
|
||||
|
||||
@@ -690,6 +690,8 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/,
|
||||
//We must update last scriptId or it looks like we reloaded a script, breaking some things such as gossip temporarily
|
||||
LastUsedScriptID = GetScriptId();
|
||||
|
||||
m_stringIds[0] = cInfo->StringId;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1811,6 +1813,8 @@ bool Creature::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap,
|
||||
// checked at creature_template loading
|
||||
m_defaultMovementType = MovementGeneratorType(data->movementType);
|
||||
|
||||
m_stringIds[1] = data->StringId;
|
||||
|
||||
if (addToMap && !GetMap()->AddToMap(this))
|
||||
return false;
|
||||
return true;
|
||||
@@ -3010,6 +3014,25 @@ uint32 Creature::GetScriptId() const
|
||||
return ASSERT_NOTNULL(sObjectMgr->GetCreatureTemplate(GetEntry()))->ScriptID;
|
||||
}
|
||||
|
||||
bool Creature::HasStringId(std::string_view id) const
|
||||
{
|
||||
return std::find(m_stringIds.begin(), m_stringIds.end(), id) != m_stringIds.end();
|
||||
}
|
||||
|
||||
void Creature::SetScriptStringId(std::string id)
|
||||
{
|
||||
if (!id.empty())
|
||||
{
|
||||
m_scriptStringId.emplace(std::move(id));
|
||||
m_stringIds[2] = *m_scriptStringId;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_scriptStringId.reset();
|
||||
m_stringIds[2] = {};
|
||||
}
|
||||
}
|
||||
|
||||
VendorItemData const* Creature::GetVendorItems() const
|
||||
{
|
||||
return sObjectMgr->GetNpcVendorItemList(GetEntry());
|
||||
|
||||
@@ -212,6 +212,9 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
|
||||
std::string const& GetAIName() const;
|
||||
std::string GetScriptName() const;
|
||||
uint32 GetScriptId() const;
|
||||
bool HasStringId(std::string_view id) const;
|
||||
void SetScriptStringId(std::string id);
|
||||
std::array<std::string_view, 3> const& GetStringIds() const { return m_stringIds; }
|
||||
|
||||
// override WorldObject function for proper name localization
|
||||
std::string GetNameForLocaleIdx(LocaleConstant locale) const override;
|
||||
@@ -433,6 +436,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
|
||||
|
||||
CreatureTemplate const* m_creatureInfo; // Can differ from sObjectMgr->GetCreatureTemplate(GetEntry()) in difficulty mode > 0
|
||||
CreatureData const* m_creatureData;
|
||||
std::array<std::string_view, 3> m_stringIds;
|
||||
Optional<std::string> m_scriptStringId;
|
||||
|
||||
uint16 m_LootMode; // Bitmask (default: LOOT_MODE_DEFAULT) that determines what loot will be lootable
|
||||
|
||||
|
||||
@@ -478,6 +478,7 @@ struct TC_GAME_API CreatureTemplate
|
||||
uint32 SpellSchoolImmuneMask;
|
||||
uint32 flags_extra;
|
||||
uint32 ScriptID;
|
||||
std::string StringId;
|
||||
WorldPacket QueryData[TOTAL_LOCALES];
|
||||
CreatureModel const* GetModelByIdx(uint32 idx) const;
|
||||
CreatureModel const* GetRandomValidModel() const;
|
||||
|
||||
@@ -2118,8 +2118,9 @@ Creature* WorldObject::FindNearestCreature(uint32 entry, float range, bool alive
|
||||
Creature* WorldObject::FindNearestCreatureWithOptions(float range, FindCreatureOptions const& options) const
|
||||
{
|
||||
Creature* creature = nullptr;
|
||||
Trinity::NearestCreatureEntryWithOptionsInObjectRangeCheck checker(*this, range, options);
|
||||
Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithOptionsInObjectRangeCheck> searcher(this, creature, checker);
|
||||
Trinity::NearestCheckCustomizer checkCustomizer(*this, range);
|
||||
Trinity::CreatureWithOptionsInObjectRangeCheck checker(*this, checkCustomizer, options);
|
||||
Trinity::CreatureLastSearcher searcher(this, creature, checker);
|
||||
if (options.IgnorePhases)
|
||||
searcher.i_phaseShift = &PhasingHandler::GetAlwaysVisiblePhaseShift();
|
||||
|
||||
@@ -3283,6 +3284,18 @@ void WorldObject::GetCreatureListWithEntryInGrid(Container& creatureContainer, u
|
||||
Cell::VisitGridObjects(this, searcher, maxSearchRange);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
void WorldObject::GetCreatureListWithOptionsInGrid(Container& creatureContainer, float maxSearchRange, FindCreatureOptions const& options) const
|
||||
{
|
||||
Trinity::NoopCheckCustomizer checkCustomizer;
|
||||
Trinity::CreatureWithOptionsInObjectRangeCheck check(*this, checkCustomizer, options);
|
||||
Trinity::CreatureListSearcher searcher(this, creatureContainer, check);
|
||||
if (options.IgnorePhases)
|
||||
searcher.i_phaseShift = &PhasingHandler::GetAlwaysVisiblePhaseShift();
|
||||
|
||||
Cell::VisitGridObjects(this, searcher, maxSearchRange);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
void WorldObject::GetPlayerListInGrid(Container& playerContainer, float maxSearchRange, bool alive /*= true*/) const
|
||||
{
|
||||
@@ -3723,6 +3736,10 @@ template TC_GAME_API void WorldObject::GetCreatureListWithEntryInGrid(std::list<
|
||||
template TC_GAME_API void WorldObject::GetCreatureListWithEntryInGrid(std::deque<Creature*>&, uint32, float) const;
|
||||
template TC_GAME_API void WorldObject::GetCreatureListWithEntryInGrid(std::vector<Creature*>&, uint32, float) const;
|
||||
|
||||
template TC_GAME_API void WorldObject::GetCreatureListWithOptionsInGrid(std::list<Creature*>&, float, FindCreatureOptions const&) const;
|
||||
template TC_GAME_API void WorldObject::GetCreatureListWithOptionsInGrid(std::deque<Creature*>&,float, FindCreatureOptions const&) const;
|
||||
template TC_GAME_API void WorldObject::GetCreatureListWithOptionsInGrid(std::vector<Creature*>&, float, FindCreatureOptions const&) const;
|
||||
|
||||
template TC_GAME_API void WorldObject::GetPlayerListInGrid(std::list<Player*>&, float, bool) const;
|
||||
template TC_GAME_API void WorldObject::GetPlayerListInGrid(std::deque<Player*>&, float, bool) const;
|
||||
template TC_GAME_API void WorldObject::GetPlayerListInGrid(std::vector<Player*>&, float, bool) const;
|
||||
|
||||
@@ -437,6 +437,7 @@ struct FindCreatureOptions
|
||||
FindCreatureOptions() = default;
|
||||
|
||||
FindCreatureOptions& SetCreatureId(uint32 creatureId) { CreatureId = creatureId; return *this; }
|
||||
FindCreatureOptions& SetStringId(std::string_view stringId) { StringId = stringId; return *this; }
|
||||
|
||||
FindCreatureOptions& SetIsAlive(bool isAlive) { IsAlive = isAlive; return *this; }
|
||||
FindCreatureOptions& SetIsInCombat(bool isInCombat) { IsInCombat = isInCombat; return *this; }
|
||||
@@ -454,6 +455,7 @@ struct FindCreatureOptions
|
||||
FindCreatureOptions& SetPrivateObjectOwner(ObjectGuid privateObjectOwnerGuid) { PrivateObjectOwnerGuid = privateObjectOwnerGuid; return *this; }
|
||||
|
||||
Optional<uint32> CreatureId;
|
||||
Optional<std::string_view> StringId;
|
||||
|
||||
Optional<bool> IsAlive;
|
||||
Optional<bool> IsInCombat;
|
||||
@@ -699,6 +701,9 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
|
||||
template <typename Container>
|
||||
void GetCreatureListWithEntryInGrid(Container& creatureContainer, uint32 entry, float maxSearchRange = 250.0f) const;
|
||||
|
||||
template <typename Container>
|
||||
void GetCreatureListWithOptionsInGrid(Container& creatureContainer, float maxSearchRange, FindCreatureOptions const& options) const;
|
||||
|
||||
template <typename Container>
|
||||
void GetPlayerListInGrid(Container& playerContainer, float maxSearchRange, bool alive = true) const;
|
||||
|
||||
|
||||
@@ -366,8 +366,8 @@ void ObjectMgr::LoadCreatureTemplates()
|
||||
// "RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, "
|
||||
// 67 68 69
|
||||
// "mechanic_immune_mask, spell_school_immune_mask, flags_extra, "
|
||||
// 70
|
||||
// "ScriptName FROM creature_template WHERE entry = ? OR 1 = ?");
|
||||
// 70 71
|
||||
// "ScriptName, StringId FROM creature_template WHERE entry = ? OR 1 = ?");
|
||||
|
||||
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEMPLATE);
|
||||
stmt->setUInt32(0, 0);
|
||||
@@ -501,6 +501,7 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields)
|
||||
creatureTemplate.SpellSchoolImmuneMask = fields[68].GetUInt32();
|
||||
creatureTemplate.flags_extra = fields[69].GetUInt32();
|
||||
creatureTemplate.ScriptID = GetScriptId(fields[70].GetString());
|
||||
creatureTemplate.StringId = fields[71].GetString();
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadCreatureTemplateResistances()
|
||||
@@ -2199,8 +2200,8 @@ void ObjectMgr::LoadCreatures()
|
||||
QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, wander_distance, "
|
||||
// 11 12 13 14 15 16 17 18 19 20 21
|
||||
"currentwaypoint, curhealth, curmana, MovementType, spawnDifficulties, eventEntry, poolSpawnId, creature.npcflag, creature.unit_flags, creature.unit_flags2, creature.unit_flags3, "
|
||||
// 22 23 24 25 26 27
|
||||
"creature.dynamicflags, creature.phaseUseFlags, creature.phaseid, creature.phasegroup, creature.terrainSwapMap, creature.ScriptName "
|
||||
// 22 23 24 25 26 27 28
|
||||
"creature.dynamicflags, creature.phaseUseFlags, creature.phaseid, creature.phasegroup, creature.terrainSwapMap, creature.ScriptName, creature.StringId "
|
||||
"FROM creature "
|
||||
"LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
|
||||
"LEFT OUTER JOIN pool_members ON pool_members.type = 0 AND creature.guid = pool_members.spawnId");
|
||||
@@ -2261,6 +2262,7 @@ void ObjectMgr::LoadCreatures()
|
||||
data.phaseGroup = fields[25].GetUInt32();
|
||||
data.terrainSwapMap = fields[26].GetInt32();
|
||||
data.scriptId = GetScriptId(fields[27].GetString());
|
||||
data.StringId = fields[28].GetString();
|
||||
data.spawnGroupData = IsTransportMap(data.mapId) ? GetLegacySpawnGroup() : GetDefaultSpawnGroup(); // transport spawns default to compatibility group
|
||||
|
||||
MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
|
||||
|
||||
@@ -686,6 +686,35 @@ namespace Trinity
|
||||
|
||||
// CHECKS && DO classes
|
||||
|
||||
// CHECK modifiers
|
||||
class NoopCheckCustomizer
|
||||
{
|
||||
public:
|
||||
bool Test(WorldObject const* /*o*/) const { return true; }
|
||||
|
||||
void Update(WorldObject const* /*o*/) { }
|
||||
};
|
||||
|
||||
class NearestCheckCustomizer
|
||||
{
|
||||
public:
|
||||
explicit NearestCheckCustomizer(WorldObject const& obj, float range) : i_obj(obj), i_range(range) { }
|
||||
|
||||
bool Test(WorldObject const* o) const
|
||||
{
|
||||
return i_obj.IsWithinDistInMap(o, i_range);
|
||||
}
|
||||
|
||||
void Update(WorldObject const* o)
|
||||
{
|
||||
i_range = i_obj.GetDistance(o);
|
||||
}
|
||||
|
||||
private:
|
||||
WorldObject const& i_obj;
|
||||
float i_range;
|
||||
};
|
||||
|
||||
// WorldObject check classes
|
||||
|
||||
class TC_GAME_API AnyDeadUnitObjectInRangeCheck
|
||||
@@ -1441,13 +1470,14 @@ namespace Trinity
|
||||
NearestCreatureEntryWithLiveStateInObjectRangeCheck(NearestCreatureEntryWithLiveStateInObjectRangeCheck const&) = delete;
|
||||
};
|
||||
|
||||
class NearestCreatureEntryWithOptionsInObjectRangeCheck
|
||||
template <typename Customizer = NoopCheckCustomizer>
|
||||
class CreatureWithOptionsInObjectRangeCheck
|
||||
{
|
||||
public:
|
||||
NearestCreatureEntryWithOptionsInObjectRangeCheck(WorldObject const& obj, float range, FindCreatureOptions const& args)
|
||||
: i_obj(obj), i_args(args), i_range(range) { }
|
||||
CreatureWithOptionsInObjectRangeCheck(WorldObject const& obj, Customizer& customizer, FindCreatureOptions const& args)
|
||||
: i_obj(obj), i_args(args), i_customizer(customizer) { }
|
||||
|
||||
bool operator()(Creature* u)
|
||||
bool operator()(Creature* u) const
|
||||
{
|
||||
if (u->getDeathState() == DEAD) // Despawned
|
||||
return false;
|
||||
@@ -1455,12 +1485,15 @@ namespace Trinity
|
||||
if (u->GetGUID() == i_obj.GetGUID())
|
||||
return false;
|
||||
|
||||
if (!i_obj.IsWithinDistInMap(u, i_range))
|
||||
if (!i_customizer.Test(u))
|
||||
return false;
|
||||
|
||||
if (i_args.CreatureId && u->GetEntry() != i_args.CreatureId)
|
||||
return false;
|
||||
|
||||
if (i_args.StringId && u->HasStringId(*i_args.StringId))
|
||||
return false;
|
||||
|
||||
if (i_args.IsAlive.has_value() && u->IsAlive() != i_args.IsAlive)
|
||||
return false;
|
||||
|
||||
@@ -1486,14 +1519,14 @@ namespace Trinity
|
||||
if (i_args.AuraSpellId && !u->HasAura(*i_args.AuraSpellId))
|
||||
return false;
|
||||
|
||||
i_range = i_obj.GetDistance(u); // use found unit range as new range limit for next check
|
||||
i_customizer.Update(u);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
WorldObject const& i_obj;
|
||||
FindCreatureOptions const& i_args;
|
||||
float i_range;
|
||||
Customizer& i_customizer;
|
||||
};
|
||||
|
||||
class AnyPlayerInObjectRangeCheck
|
||||
|
||||
@@ -111,6 +111,7 @@ struct SpawnData : public SpawnMetadata
|
||||
int32 spawntimesecs = 0;
|
||||
std::vector<Difficulty> spawnDifficulties;
|
||||
uint32 scriptId = 0;
|
||||
std::string StringId;
|
||||
|
||||
protected:
|
||||
SpawnData(SpawnObjectType t) : SpawnMetadata(t) {}
|
||||
|
||||
@@ -1148,8 +1148,9 @@ enum TrinityStrings
|
||||
LANG_NPCINFO_NPC_FLAGS = 5086,
|
||||
LANG_NPCINFO_PHASE_IDS = 5087,
|
||||
LANG_SCENARIO = 5088,
|
||||
LANG_OBJECTINFO_STRINGIDS = 5089,
|
||||
|
||||
// Room for more Trinity strings 5089-6603
|
||||
// Room for more Trinity strings 5090-6603
|
||||
|
||||
// Level requirement notifications
|
||||
LANG_SAY_REQ = 6604,
|
||||
|
||||
@@ -535,6 +535,8 @@ public:
|
||||
handler->PSendSysMessage(LANG_NPCINFO_ARMOR, target->GetArmor());
|
||||
handler->PSendSysMessage(LANG_NPCINFO_POSITION, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
|
||||
handler->PSendSysMessage(LANG_OBJECTINFO_AIINFO, target->GetAIName().c_str(), target->GetScriptName().c_str());
|
||||
handler->PSendSysMessage(LANG_OBJECTINFO_STRINGIDS, STRING_VIEW_FMT_ARG(target->GetStringIds()[0]),
|
||||
STRING_VIEW_FMT_ARG(target->GetStringIds()[1]), STRING_VIEW_FMT_ARG(target->GetStringIds()[2]));
|
||||
handler->PSendSysMessage(LANG_NPCINFO_REACTSTATE, DescribeReactState(target->GetReactState()));
|
||||
if (CreatureAI const* ai = target->AI())
|
||||
handler->PSendSysMessage(LANG_OBJECTINFO_AITYPE, GetTypeName(*ai).c_str());
|
||||
|
||||
Reference in New Issue
Block a user