diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 240e0eeab..753ba9022 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -1267,10 +1267,14 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u } case SMART_ACTION_SUMMON_CREATURE: { - WorldObject* summoner = GetBaseObject() ? GetBaseObject() : unit; + EnumFlag flags(static_cast(e.action.summonCreature.flags)); + bool preferUnit = flags.HasFlag(SmartActionSummonCreatureFlags::PreferUnit); + WorldObject* summoner = preferUnit ? unit : Coalesce(GetBaseObject(), unit); if (!summoner) break; + bool personalSpawn = flags.HasFlag(SmartActionSummonCreatureFlags::PersonalSpawn); + if (e.GetTargetType() == SMART_TARGET_RANDOM_POINT) { float range = (float)e.target.randomPoint.range; @@ -1282,7 +1286,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u randomPoint = me->GetRandomPoint(me->GetPosition(), range); else randomPoint = me->GetRandomPoint(srcPos, range); - if (Creature* summon = summoner->SummonCreature(e.action.summonCreature.creature, randomPoint, (TempSummonType)e.action.summonCreature.type, e.action.summonCreature.duration)) + if (Creature* summon = summoner->SummonCreature(e.action.summonCreature.creature, randomPoint, (TempSummonType)e.action.summonCreature.type, e.action.summonCreature.duration, 0, nullptr, personalSpawn)) { if (unit && e.action.summonCreature.attackInvoker) summon->AI()->AttackStart(unit); @@ -1301,7 +1305,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u y += e.target.y; z += e.target.z; o += e.target.o; - if (Creature* summon = summoner->SummonCreature(e.action.summonCreature.creature, x, y, z, o, (TempSummonType)e.action.summonCreature.type, e.action.summonCreature.duration)) + if (Creature* summon = summoner->SummonCreature(e.action.summonCreature.creature, x, y, z, o, (TempSummonType)e.action.summonCreature.type, e.action.summonCreature.duration, nullptr, personalSpawn)) { if (e.action.summonCreature.attackInvoker == 2) // pussywizard: proper attackInvoker implementation summon->AI()->AttackStart(unit); diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index f37577f17..8e670c191 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -534,7 +534,7 @@ enum SMART_ACTION SMART_ACTION_ACTIVATE_GOBJECT = 9, // SMART_ACTION_RANDOM_EMOTE = 10, // EmoteId1, EmoteId2, EmoteId3... SMART_ACTION_CAST = 11, // SpellId, CastFlags, LimitTargets - SMART_ACTION_SUMMON_CREATURE = 12, // CreatureID, summonType, duration in ms, attackInvoker, attackScriptOwner + SMART_ACTION_SUMMON_CREATURE = 12, // CreatureID, summonType, duration in ms, attackInvoker, attackScriptOwner, flags(SmartActionSummonCreatureFlags) SMART_ACTION_THREAT_SINGLE_PCT = 13, // Threat% SMART_ACTION_THREAT_ALL_PCT = 14, // Threat% SMART_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS = 15, // QuestID @@ -696,6 +696,17 @@ enum SMART_ACTION SMART_ACTION_AC_END = 225, // placeholder }; +enum class SmartActionSummonCreatureFlags +{ + None = 0, + PersonalSpawn = 1, + PreferUnit = 2, + + All = PersonalSpawn | PreferUnit +}; + +DEFINE_ENUM_FLAG(SmartActionSummonCreatureFlags); + struct SmartAction { SMART_ACTION type; @@ -820,6 +831,7 @@ struct SmartAction uint32 duration; SAIBool attackInvoker; uint32 attackScriptOwner; + uint32 flags; // SmartActionSummonCreatureFlags } summonCreature; struct diff --git a/src/server/game/Combat/ThreatMgr.cpp b/src/server/game/Combat/ThreatMgr.cpp index 91751f22a..87e9725a4 100644 --- a/src/server/game/Combat/ThreatMgr.cpp +++ b/src/server/game/Combat/ThreatMgr.cpp @@ -454,11 +454,27 @@ void ThreatMgr::AddThreat(Unit* victim, float threat, SpellSchoolMask schoolMask void ThreatMgr::DoAddThreat(Unit* victim, float threat) { uint32 redirectThreadPct = victim->GetRedirectThreatPercent(); + Unit* redirectTarget = victim->GetRedirectThreatTarget(); + + // Personal Spawns from same summoner can aggro each other + if (TempSummon* tempSummonVictim = victim->ToTempSummon()) + { + if (tempSummonVictim->IsVisibleBySummonerOnly()) + { + if (!GetOwner()->ToTempSummon() || + !GetOwner()->ToTempSummon()->IsVisibleBySummonerOnly() || + tempSummonVictim->GetSummonerGUID() != GetOwner()->ToTempSummon()->GetSummonerGUID()) + { + redirectThreadPct = 100; + redirectTarget = tempSummonVictim->GetSummonerUnit(); + } + } + } // must check > 0.0f, otherwise dead loop if (threat > 0.0f && redirectThreadPct) { - if (Unit* redirectTarget = victim->GetRedirectThreatTarget()) + if (redirectTarget) { float redirectThreat = CalculatePct(threat, redirectThreadPct); threat -= redirectThreat; diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index a741d9638..57957734c 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -25,7 +25,7 @@ TempSummon::TempSummon(SummonPropertiesEntry const* properties, ObjectGuid owner, bool isWorldObject) : Creature(isWorldObject), m_Properties(properties), m_type(TEMPSUMMON_MANUAL_DESPAWN), - m_timer(0), m_lifetime(0) + m_timer(0), m_lifetime(0), _visibleBySummonerOnly(false) { if (owner) { diff --git a/src/server/game/Entities/Creature/TemporarySummon.h b/src/server/game/Entities/Creature/TemporarySummon.h index 3e005270f..e5bc369bd 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.h +++ b/src/server/game/Entities/Creature/TemporarySummon.h @@ -57,14 +57,19 @@ public: uint32 GetTimer() { return m_timer; } void SetTimer(uint32 t) { m_timer = t; } + void SetVisibleBySummonerOnly(bool visibleBySummonerOnly) { _visibleBySummonerOnly = visibleBySummonerOnly; } + [[nodiscard]] bool IsVisibleBySummonerOnly() const { return _visibleBySummonerOnly; } + const SummonPropertiesEntry* const m_Properties; std::string GetDebugInfo() const override; + private: TempSummonType m_type; uint32 m_timer; uint32 m_lifetime; ObjectGuid m_summonerGUID; + bool _visibleBySummonerOnly; }; class Minion : public TempSummon diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 8c8511c5a..2f1e6bc27 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1773,6 +1773,17 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo WorldObject const* viewpoint = this; if (Player const* thisPlayer = ToPlayer()) { + if (Creature const* creature = obj->ToCreature()) + { + if (TempSummon const* tempSummon = creature->ToTempSummon()) + { + if (tempSummon->IsVisibleBySummonerOnly() && GetGUID() != tempSummon->GetSummonerGUID()) + { + return false; + } + } + } + if (thisPlayer->isDead() && thisPlayer->GetHealth() > 0 && // Cheap way to check for ghost state !(obj->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GHOST) & m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GHOST) & GHOST_VISIBILITY_GHOST)) { @@ -2115,7 +2126,7 @@ void WorldObject::AddObjectToRemoveList() map->AddObjectToRemoveList(this); } -TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropertiesEntry const* properties /*= nullptr*/, uint32 duration /*= 0*/, WorldObject* summoner /*= nullptr*/, uint32 spellId /*= 0*/, uint32 vehId /*= 0*/) +TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropertiesEntry const* properties /*= nullptr*/, uint32 duration /*= 0*/, WorldObject* summoner /*= nullptr*/, uint32 spellId /*= 0*/, uint32 vehId /*= 0*/, bool visibleBySummonerOnly /*= false*/) { uint32 mask = UNIT_MASK_SUMMON; if (properties) @@ -2204,6 +2215,9 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropert summon->SetHomePosition(pos); summon->InitStats(duration); + + summon->SetVisibleBySummonerOnly(visibleBySummonerOnly); + if (!AddToMap(summon->ToCreature(), summon->GetOwnerGUID().IsPlayer() || (summoner && summoner->GetTransport()))) { delete summon; @@ -2236,7 +2250,7 @@ void Map::SummonCreatureGroup(uint8 group, std::list* list /*= null list->push_back(summon); } -TempSummon* WorldObject::SummonCreature(uint32 id, float x, float y, float z, float ang, TempSummonType spwtype, uint32 despwtime, SummonPropertiesEntry const* properties) +TempSummon* WorldObject::SummonCreature(uint32 id, float x, float y, float z, float ang, TempSummonType spwtype, uint32 despwtime, SummonPropertiesEntry const* properties, bool visibleBySummonerOnly) { if (!x && !y && !z) { @@ -2245,7 +2259,7 @@ TempSummon* WorldObject::SummonCreature(uint32 id, float x, float y, float z, fl } Position pos; pos.Relocate(x, y, z, ang); - return SummonCreature(id, pos, spwtype, despwtime, 0, properties); + return SummonCreature(id, pos, spwtype, despwtime, 0, properties, visibleBySummonerOnly); } GameObject* Map::SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime, bool checkTransport) @@ -2301,11 +2315,11 @@ void WorldObject::ClearZoneScript() m_zoneScript = nullptr; } -TempSummon* WorldObject::SummonCreature(uint32 entry, const Position& pos, TempSummonType spwtype, uint32 duration, uint32 /*vehId*/, SummonPropertiesEntry const* properties) const +TempSummon* WorldObject::SummonCreature(uint32 entry, const Position& pos, TempSummonType spwtype, uint32 duration, uint32 /*vehId*/, SummonPropertiesEntry const* properties, bool visibleBySummonerOnly /*= false*/) const { if (Map* map = FindMap()) { - if (TempSummon* summon = map->SummonCreature(entry, pos, properties, duration, (WorldObject*) this)) + if (TempSummon* summon = map->SummonCreature(entry, pos, properties, duration, (WorldObject*)this, 0, 0, visibleBySummonerOnly)) { summon->SetTempSummonType(spwtype); return summon; diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index da57d6f04..ac72ba3b0 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -522,8 +522,8 @@ public: void ClearZoneScript(); [[nodiscard]] ZoneScript* GetZoneScript() const { return m_zoneScript; } - TempSummon* SummonCreature(uint32 id, const Position& pos, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, uint32 vehId = 0, SummonPropertiesEntry const* properties = nullptr) const; - TempSummon* SummonCreature(uint32 id, float x, float y, float z, float ang = 0, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, SummonPropertiesEntry const* properties = nullptr); + TempSummon* SummonCreature(uint32 id, const Position& pos, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, uint32 vehId = 0, SummonPropertiesEntry const* properties = nullptr, bool visibleBySummonerOnly = false) const; + TempSummon* SummonCreature(uint32 id, float x, float y, float z, float ang = 0, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, SummonPropertiesEntry const* properties = nullptr, bool visibleBySummonerOnly = false); GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime, bool checkTransport = true, GOSummonType summonType = GO_SUMMON_TIMED_OR_CORPSE_DESPAWN); Creature* SummonTrigger(float x, float y, float z, float ang, uint32 dur, bool setLevel = false, CreatureAI * (*GetAI)(Creature*) = nullptr); void SummonCreatureGroup(uint8 group, std::list* list = nullptr); diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 2b3b51b44..46871dfbb 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -597,7 +597,7 @@ namespace Acore void Visit(PlayerMapType& m) { for (PlayerMapType::iterator itr = m.begin(); itr != m.end(); ++itr) - if (itr->GetSource()->InSamePhase(i_searcher) && itr->GetSource()->IsWithinDist(i_searcher, i_dist)) + if (itr->GetSource()->HaveAtClient(i_searcher) && itr->GetSource()->IsWithinDist(i_searcher, i_dist)) i_do(itr->GetSource()); } diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 0f5f73929..0bf6b9c18 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -500,7 +500,7 @@ public: void UpdateIteratorBack(Player* player); - TempSummon* SummonCreature(uint32 entry, Position const& pos, SummonPropertiesEntry const* properties = nullptr, uint32 duration = 0, WorldObject* summoner = nullptr, uint32 spellId = 0, uint32 vehId = 0); + TempSummon* SummonCreature(uint32 entry, Position const& pos, SummonPropertiesEntry const* properties = nullptr, uint32 duration = 0, WorldObject* summoner = nullptr, uint32 spellId = 0, uint32 vehId = 0, bool visibleBySummonerOnly = false); GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime, bool checkTransport = true); GameObject* SummonGameObject(uint32 entry, Position const& pos, float rotation0 = 0.0f, float rotation1 = 0.0f, float rotation2 = 0.0f, float rotation3 = 0.0f, uint32 respawnTime = 100, bool checkTransport = true); void SummonCreatureGroup(uint8 group, std::list* list = nullptr); diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index f28361b88..e6e302460 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -753,7 +753,7 @@ public: HitTriggerSpellList m_hitTriggerSpells; // effect helpers - void SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties, uint32 numSummons); + void SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties, uint32 numSummons, bool personalSpawn); void CalculateJumpSpeeds(uint8 i, float dist, float& speedxy, float& speedz); SpellCastResult CanOpenLock(uint32 effIndex, uint32 lockid, SkillType& skillid, int32& reqSkillValue, int32& skillValue); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 7b7995f3c..4c4b6d6e7 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2354,6 +2354,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) if (!m_originalCaster) return; + bool personalSpawn = (properties->Flags & SUMMON_PROP_FLAG_ONLY_VISIBLE_TO_SUMMONER) != 0; int32 duration = m_spellInfo->GetDuration(); if (Player* modOwner = m_originalCaster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); @@ -2398,7 +2399,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) case SUMMON_CATEGORY_UNK: if (properties->Flags & 512) { - SummonGuardian(effIndex, entry, properties, numSummons); + SummonGuardian(effIndex, entry, properties, numSummons, personalSpawn); break; } switch (properties->Type) @@ -2407,18 +2408,18 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) case SUMMON_TYPE_GUARDIAN: case SUMMON_TYPE_GUARDIAN2: case SUMMON_TYPE_MINION: - SummonGuardian(effIndex, entry, properties, numSummons); + SummonGuardian(effIndex, entry, properties, numSummons, personalSpawn); break; // Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE) case SUMMON_TYPE_VEHICLE: case SUMMON_TYPE_VEHICLE2: - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id); + summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); break; case SUMMON_TYPE_LIGHTWELL: case SUMMON_TYPE_TOTEM: { // protection code - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id); + summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); if (!summon || !summon->IsTotem()) return; @@ -2436,7 +2437,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) case SUMMON_TYPE_JEEVES: case SUMMON_TYPE_MINIPET: { - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id); + summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); if (!summon || !summon->HasUnitTypeMask(UNIT_MASK_MINION)) return; @@ -2470,7 +2471,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) // randomize position for multiple summons pos = m_caster->GetRandomPoint(*destTarget, radius); - summon = m_originalCaster->SummonCreature(entry, pos, summonType, duration); + summon = m_originalCaster->SummonCreature(entry, pos, summonType, duration, 0, nullptr, personalSpawn); if (!summon) continue; @@ -2492,10 +2493,10 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) // Xinef: SummonGuardian function can summon a few npcs of same type, remove old summons with same entry here if (m_originalCaster) m_originalCaster->RemoveAllMinionsByEntry(entry); - SummonGuardian(effIndex, entry, properties, numSummons); + SummonGuardian(effIndex, entry, properties, numSummons, personalSpawn); break; case SUMMON_CATEGORY_PUPPET: - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id); + summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); break; case SUMMON_CATEGORY_VEHICLE: // Summoning spells (usually triggered by npc_spellclick) that spawn a vehicle and that cause the clicker @@ -2506,7 +2507,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) if (std::fabs(m_caster->GetPositionZ() - destTarget->GetPositionZ()) > 6.0f) destTarget->m_positionZ = m_caster->GetPositionZ(); - summon = m_originalCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_caster, m_spellInfo->Id); + summon = m_originalCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_caster, m_spellInfo->Id, 0, personalSpawn); if (!summon || !summon->IsVehicle()) return; @@ -3176,7 +3177,7 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) // Xinef: unsummon old guardian if (Guardian* oldPet = m_originalCaster->GetGuardianPet()) oldPet->UnSummon(); - SummonGuardian(effIndex, petentry, properties, 1); + SummonGuardian(effIndex, petentry, properties, 1, false); } return; } @@ -6005,7 +6006,7 @@ void Spell::EffectGameObjectSetDestructionState(SpellEffIndex effIndex) gameObjTarget->SetDestructibleState(GameObjectDestructibleState(m_spellInfo->Effects[effIndex].MiscValue), player, true); } -void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties, uint32 numGuardians) +void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties, uint32 numGuardians, bool personalSpawn) { Unit* caster = m_originalCaster; if (!caster) @@ -6105,7 +6106,7 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* pos = m_caster->GetRandomPoint(*destTarget, radius); } - summon = map->SummonCreature(entry, pos, properties, duration, caster, m_spellInfo->Id); + summon = map->SummonCreature(entry, pos, properties, duration, caster, m_spellInfo->Id, 0, personalSpawn); if (!summon) return; diff --git a/src/server/shared/DataStores/DBCEnums.h b/src/server/shared/DataStores/DBCEnums.h index 4b6e31204..00430847e 100644 --- a/src/server/shared/DataStores/DBCEnums.h +++ b/src/server/shared/DataStores/DBCEnums.h @@ -426,22 +426,22 @@ enum SummonPropGroup enum SummonPropFlags { SUMMON_PROP_FLAG_NONE = 0x00000000, // 1342 spells in 3.0.3 - SUMMON_PROP_FLAG_ATTACK_SUMMONER = 0x00000001, // 75 spells in 3.0.3, something unfriendly TODO: Needs implementation - SUMMON_PROP_FLAG_ASSIST_COMBAT_SUMMON = 0x00000002, // 616 spells in 3.0.3, something friendly TODO: Needs implementation - SUMMON_PROP_FLAG_USE_LEVEL_OFFSET = 0x00000004, // 22 spells in 3.0.3, no idea... TODO: Needs implementation - SUMMON_PROP_FLAG_DESPAWN_ON_SUMMONER_DEATH = 0x00000008, // 49 spells in 3.0.3, some mounts TODO: Needs implementation - SUMMON_PROP_FLAG_ONLY_VISIBLE_TO_SUMMONER = 0x00000010, // 25 spells in 3.0.3, quest related? TODO: Needs implementation - SUMMON_PROP_FLAG_CANNOT_DISMISS_PET = 0x00000020, // 0 spells in 3.3.5, unused TODO: Needs implementation - SUMMON_PROP_FLAG_USE_DEMON_TIMEOUT = 0x00000040, // 12 spells in 3.0.3, no idea TODO: Needs implementation - SUMMON_PROP_FLAG_UNLIMITED_SUMMONS = 0x00000080, // 4 spells in 3.0.3, no idea TODO: Needs implementation - SUMMON_PROP_FLAG_USE_CREATURE_LEVEL = 0x00000100, // 51 spells in 3.0.3, no idea, many quest related TODO: Needs implementation - SUMMON_PROP_FLAG_JOIN_SUMMONER_SPAWN_GROUP = 0x00000200, // 51 spells in 3.0.3, something defensive TODO: Needs implementation - SUMMON_PROP_FLAG_DO_NOT_TOGGLE = 0x00000400, // 3 spells, requires something near? TODO: Needs implementation - SUMMON_PROP_FLAG_DESPAWN_WHEN_EXPIRED = 0x00000800, // 30 spells in 3.0.3, no idea TODO: Needs implementation - SUMMON_PROP_FLAG_USE_SUMMONER_FACTION = 0x00001000, // Lightwell, Jeeves, Gnomish Alarm-o-bot, Build vehicles(wintergrasp) TODO: Needs implementation - SUMMON_PROP_FLAG_DO_NOT_FOLLOW_MOUNTED_SUMMONER = 0x00002000, // Guides, player follows TODO: Needs implementation - SUMMON_PROP_FLAG_SAVE_PET_AUTOCAST = 0x00004000, // Force of Nature, Shadowfiend, Feral Spirit, Summon Water Elemental TODO: Needs implementation - SUMMON_PROP_FLAG_IGNORE_SUMMONER_PHASE = 0x00008000, // Light/Dark Bullet, Soul/Fiery Consumption, Twisted Visage, Twilight Whelp. Phase related? TODO: Needs implementation + SUMMON_PROP_FLAG_ATTACK_SUMMONER = 0x00000001, // NYI 75 spells in 3.0.3, something unfriendly + SUMMON_PROP_FLAG_ASSIST_COMBAT_SUMMON = 0x00000002, // NYI 616 spells in 3.0.3, something friendly + SUMMON_PROP_FLAG_USE_LEVEL_OFFSET = 0x00000004, // NYI 22 spells in 3.0.3, no idea... + SUMMON_PROP_FLAG_DESPAWN_ON_SUMMONER_DEATH = 0x00000008, // NYI 49 spells in 3.0.3, some mounts + SUMMON_PROP_FLAG_ONLY_VISIBLE_TO_SUMMONER = 0x00000010, // 25 spells in 3.0.3, quest related? + SUMMON_PROP_FLAG_CANNOT_DISMISS_PET = 0x00000020, // NYI 0 spells in 3.3.5, unused + SUMMON_PROP_FLAG_USE_DEMON_TIMEOUT = 0x00000040, // NYI 12 spells in 3.0.3, no idea + SUMMON_PROP_FLAG_UNLIMITED_SUMMONS = 0x00000080, // NYI 4 spells in 3.0.3, no idea + SUMMON_PROP_FLAG_USE_CREATURE_LEVEL = 0x00000100, // NYI 51 spells in 3.0.3, no idea, many quest related + SUMMON_PROP_FLAG_JOIN_SUMMONER_SPAWN_GROUP = 0x00000200, // NYI 51 spells in 3.0.3, something defensive + SUMMON_PROP_FLAG_DO_NOT_TOGGLE = 0x00000400, // NYI 3 spells, requires something near? + SUMMON_PROP_FLAG_DESPAWN_WHEN_EXPIRED = 0x00000800, // NYI 30 spells in 3.0.3, no idea + SUMMON_PROP_FLAG_USE_SUMMONER_FACTION = 0x00001000, // NYI Lightwell, Jeeves, Gnomish Alarm-o-bot, Build vehicles(wintergrasp) + SUMMON_PROP_FLAG_DO_NOT_FOLLOW_MOUNTED_SUMMONER = 0x00002000, // NYI Guides, player follows + SUMMON_PROP_FLAG_SAVE_PET_AUTOCAST = 0x00004000, // NYI Force of Nature, Shadowfiend, Feral Spirit, Summon Water Elemental + SUMMON_PROP_FLAG_IGNORE_SUMMONER_PHASE = 0x00008000, // NYI Light/Dark Bullet, Soul/Fiery Consumption, Twisted Visage, Twilight Whelp. Phase related? }; enum VehicleSeatFlags