diff options
| author | r00ty-tc <r00ty-tc@users.noreply.github.com> | 2017-05-07 21:48:41 +0100 |
|---|---|---|
| committer | Treeston <treeston.mmoc@gmail.com> | 2017-07-31 21:21:04 +0200 |
| commit | 59db2eeea0a35028779fd76372ae06cc98c8086f (patch) | |
| tree | a9532989868948cb309cb8d76a2e76a017fa9246 /src/server/game/AI | |
| parent | d24ce1739a799042d5a164794c09674227c8572c (diff) | |
Dynamic Creature/Go spawning:
- True blizzlike creature spawn/respawn behavior - new creature = new object
- Toggleable spawn groups (with C++/SAI/command options to use them)
- Custom feature: dynamic spawn rate scaling. Accelerates respawn rate based on players in the zone.
- Backward compatibility mode (set via group and for summons)
to support creatures/gos that currently don't work well with this
(this should be removed once the exceptions are fixed)
Fixes and closes #2858
Tags #8661 as fixable.
Fixes and closes #13787
Fixes #15222.
Diffstat (limited to 'src/server/game/AI')
| -rw-r--r-- | src/server/game/AI/CreatureAI.h | 3 | ||||
| -rw-r--r-- | src/server/game/AI/ScriptedAI/ScriptedCreature.cpp | 10 | ||||
| -rw-r--r-- | src/server/game/AI/ScriptedAI/ScriptedCreature.h | 4 | ||||
| -rw-r--r-- | src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp | 41 | ||||
| -rw-r--r-- | src/server/game/AI/ScriptedAI/ScriptedEscortAI.h | 1 | ||||
| -rw-r--r-- | src/server/game/AI/SmartScripts/SmartScript.cpp | 86 | ||||
| -rw-r--r-- | src/server/game/AI/SmartScripts/SmartScriptMgr.cpp | 6 | ||||
| -rw-r--r-- | src/server/game/AI/SmartScripts/SmartScriptMgr.h | 21 |
8 files changed, 160 insertions, 12 deletions
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index bd7a6efab22..c355eaacb58 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -187,6 +187,9 @@ class TC_GAME_API CreatureAI : public UnitAI // If a PlayerAI* is returned, that AI is placed on the player instead of the default charm AI // Object destruction is handled by Unit::RemoveCharmedBy virtual PlayerAI* GetAIForCharmedPlayer(Player* /*who*/) { return nullptr; } + // Should return true if the NPC is target of an escort quest + // If onlyIfActive is set, should return true only if the escort quest is currently active + virtual bool IsEscortNPC(bool /*onlyIfActive*/) const { return false; } // intended for encounter design/debugging. do not use for other purposes. expensive. int32 VisualizeBoundary(uint32 duration, Unit* owner = nullptr, bool fill = false) const; diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index e9e252bcf35..afa7e789b7a 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -478,7 +478,7 @@ void BossAI::_Reset() events.Reset(); summons.DespawnAll(); scheduler.CancelAll(); - if (instance) + if (instance && instance->GetBossState(_bossId) != DONE) instance->SetBossState(_bossId, NOT_STARTED); } @@ -564,12 +564,12 @@ bool BossAI::CanAIAttack(Unit const* target) const return CheckBoundary(target); } -void BossAI::_DespawnAtEvade(uint32 delayToRespawn /*= 30*/, Creature* who /*= nullptr*/) +void BossAI::_DespawnAtEvade(Seconds delayToRespawn, Creature* who) { - if (delayToRespawn < 2) + if (delayToRespawn < Seconds(2)) { - TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of %u seconds, defaulting to 2.", delayToRespawn); - delayToRespawn = 2; + TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of %ld seconds, defaulting to 2.", delayToRespawn.count()); + delayToRespawn = Seconds(2); } if (!who) diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 6fc85b6a81a..9f2e49c1ffb 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -366,8 +366,8 @@ class TC_GAME_API BossAI : public ScriptedAI void _EnterCombat(); void _JustDied(); void _JustReachedHome(); - void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr); - void _DespawnAtEvade(Seconds const& time, Creature* who = nullptr) { _DespawnAtEvade(uint32(time.count()), who); } + void _DespawnAtEvade(Seconds delayToRespawn, Creature* who = nullptr); + void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr) { _DespawnAtEvade(Seconds(delayToRespawn), who); } void TeleportCheaters(); diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index bcf060e2ad6..5f5330c3c8d 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -239,13 +239,17 @@ void npc_escortAI::UpdateAI(uint32 diff) return; } - if (m_bCanInstantRespawn) + if (m_bCanInstantRespawn && !sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC)) { me->setDeathState(JUST_DIED); me->Respawn(); } else + { + if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC)) + me->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, me->GetSpawnId(), true); me->DespawnOrUnsummon(); + } return; } @@ -280,11 +284,17 @@ void npc_escortAI::UpdateAI(uint32 diff) { TC_LOG_DEBUG("scripts", "EscortAI failed because player/group was to far away or not found"); - if (m_bCanInstantRespawn) + bool isEscort = false; + if (CreatureData const* cdata = me->GetCreatureData()) + isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC)); + + if (m_bCanInstantRespawn && !isEscort) { me->setDeathState(JUST_DIED); me->Respawn(); } + else if (m_bCanInstantRespawn && isEscort) + me->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, me->GetSpawnId(), true); else me->DespawnOrUnsummon(); @@ -424,6 +434,22 @@ void npc_escortAI::SetRun(bool on) /// @todo get rid of this many variables passed in function. void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */) { + // Queue respawn from the point it starts + if (Map* map = me->GetMap()) + { + if (CreatureData const* cdata = me->GetCreatureData()) + { + if (SpawnGroupTemplateData const* groupdata = cdata->spawnGroupData) + { + if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (groupdata->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC) && !map->GetCreatureRespawnTime(me->GetSpawnId())) + { + me->SetRespawnTime(me->GetRespawnDelay()); + me->SaveRespawnTime(); + } + } + } + } + if (me->GetVictim()) { TC_LOG_ERROR("scripts.escortai", "TSCR ERROR: EscortAI (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry()); @@ -567,3 +593,14 @@ bool npc_escortAI::GetWaypointPosition(uint32 pointId, float& x, float& y, float return false; } + +bool npc_escortAI::IsEscortNPC(bool onlyIfActive) const +{ + if (!onlyIfActive) + return true; + + if (GetEventStarterGUID()) + return true; + + return false; +} diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h index 754d96dced9..7d6f210f034 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h @@ -107,6 +107,7 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI bool GetAttack() const { return m_bIsActiveAttacker; }//used in EnterEvadeMode override void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; } ObjectGuid GetEventStarterGUID() const { return m_uiPlayerGUID; } + virtual bool IsEscortNPC(bool isEscorting) const override; protected: Player* GetPlayerForEscort(); diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 03ad4df397a..79f3b7ce652 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -2052,6 +2052,92 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (IsCreature(target)) target->ToCreature()->SetCorpseDelay(e.action.corpseDelay.timer); } + + break; + } + case SMART_ACTION_SPAWN_SPAWNGROUP: + { + if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0) + { + bool const ignoreRespawn = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN) != 0); + bool const force = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_FORCE_SPAWN) != 0); + + // Instant spawn + sObjectMgr->SpawnGroupSpawn(e.action.groupSpawn.groupId, GetBaseObject()->GetMap(), ignoreRespawn, force); + } + else + { + // Delayed spawn (use values from parameter to schedule event to call us back + SmartEvent ne = SmartEvent(); + ne.type = (SMART_EVENT)SMART_EVENT_UPDATE; + ne.event_chance = 100; + + ne.minMaxRepeat.min = e.action.groupSpawn.minDelay; + ne.minMaxRepeat.max = e.action.groupSpawn.maxDelay; + ne.minMaxRepeat.repeatMin = 0; + ne.minMaxRepeat.repeatMax = 0; + + ne.event_flags = 0; + ne.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE; + + SmartAction ac = SmartAction(); + ac.type = (SMART_ACTION)SMART_ACTION_SPAWN_SPAWNGROUP; + ac.groupSpawn.groupId = e.action.groupSpawn.groupId; + ac.groupSpawn.minDelay = 0; + ac.groupSpawn.maxDelay = 0; + ac.groupSpawn.spawnflags = e.action.groupSpawn.spawnflags; + ac.timeEvent.id = e.action.timeEvent.id; + + SmartScriptHolder ev = SmartScriptHolder(); + ev.event = ne; + ev.event_id = e.event_id; + ev.target = e.target; + ev.action = ac; + InitTimer(ev); + mStoredEvents.push_back(ev); + } + break; + } + case SMART_ACTION_DESPAWN_SPAWNGROUP: + { + if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0) + { + bool const deleteRespawnTimes = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN) != 0); + + // Instant spawn + sObjectMgr->SpawnGroupDespawn(e.action.groupSpawn.groupId, GetBaseObject()->GetMap(), deleteRespawnTimes); + } + else + { + // Delayed spawn (use values from parameter to schedule event to call us back + SmartEvent ne = SmartEvent(); + ne.type = (SMART_EVENT)SMART_EVENT_UPDATE; + ne.event_chance = 100; + + ne.minMaxRepeat.min = e.action.groupSpawn.minDelay; + ne.minMaxRepeat.max = e.action.groupSpawn.maxDelay; + ne.minMaxRepeat.repeatMin = 0; + ne.minMaxRepeat.repeatMax = 0; + + ne.event_flags = 0; + ne.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE; + + SmartAction ac = SmartAction(); + ac.type = (SMART_ACTION)SMART_ACTION_DESPAWN_SPAWNGROUP; + ac.groupSpawn.groupId = e.action.groupSpawn.groupId; + ac.groupSpawn.minDelay = 0; + ac.groupSpawn.maxDelay = 0; + ac.groupSpawn.spawnflags = e.action.groupSpawn.spawnflags; + ac.timeEvent.id = e.action.timeEvent.id; + + SmartScriptHolder ev = SmartScriptHolder(); + ev.event = ne; + ev.event_id = e.event_id; + ev.target = e.target; + ev.action = ac; + InitTimer(ev); + mStoredEvents.push_back(ev); + } break; } case SMART_ACTION_DISABLE_EVADE: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 31282a934ac..3555575b2b2 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -228,7 +228,7 @@ void SmartAIMgr::LoadSmartAIFromDB() } case SMART_SCRIPT_TYPE_GAMEOBJECT: { - GameObjectData const* gameObject = sObjectMgr->GetGOData(uint32(std::abs(temp.entryOrGuid))); + GameObjectData const* gameObject = sObjectMgr->GetGameObjectData(uint32(std::abs(temp.entryOrGuid))); if (!gameObject) { TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: GameObject guid (%u) does not exist, skipped loading.", uint32(std::abs(temp.entryOrGuid))); @@ -932,7 +932,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) return false; } - if (e.event.distance.guid != 0 && !sObjectMgr->GetGOData(e.event.distance.guid)) + if (e.event.distance.guid != 0 && !sObjectMgr->GetGameObjectData(e.event.distance.guid)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject guid %u, skipped.", e.event.distance.guid); return false; @@ -1508,6 +1508,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) case SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT: case SMART_ACTION_SET_COUNTER: case SMART_ACTION_REMOVE_ALL_GAMEOBJECTS: + case SMART_ACTION_SPAWN_SPAWNGROUP: + case SMART_ACTION_DESPAWN_SPAWNGROUP: break; default: TC_LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry %d SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id); diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 7a1433bc7fb..f2ba244aaae 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -589,8 +589,12 @@ enum SMART_ACTION SMART_ACTION_REMOVE_ALL_GAMEOBJECTS = 126, SMART_ACTION_STOP_MOTION = 127, // stopMoving, movementExpired SMART_ACTION_PLAY_ANIMKIT = 128, // don't use on 3.3.5a + SMART_ACTION_SCENE_PLAY = 129, // don't use on 3.3.5a + SMART_ACTION_SCENE_CANCEL = 130, // don't use on 3.3.5a + SMART_ACTION_SPAWN_SPAWNGROUP = 131, // Group ID, min secs, max secs, spawnflags + SMART_ACTION_DESPAWN_SPAWNGROUP = 132, // Group ID, min secs, max secs, spawnflags - SMART_ACTION_END = 129 + SMART_ACTION_END = 133 }; struct SmartAction @@ -1094,6 +1098,13 @@ struct SmartAction { uint32 disable; } disableEvade; + struct + { + uint32 groupId; + uint32 minDelay; + uint32 maxDelay; + uint32 spawnflags; + } groupSpawn; struct { @@ -1138,6 +1149,14 @@ struct SmartAction }; }; +enum SMARTAI_SPAWN_FLAGS +{ + SMARTAI_SPAWN_FLAG_NONE = 0x00, + SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN = 0x01, + SMARTAI_SPAWN_FLAG_FORCE_SPAWN = 0x02, + SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN = 0x04, +}; + enum SMARTAI_TEMPLATE { SMARTAI_TEMPLATE_BASIC = 0, //nothing is preset |
