aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/AI
diff options
context:
space:
mode:
authorr00ty-tc <r00ty-tc@users.noreply.github.com>2017-05-07 21:48:41 +0100
committerTreeston <treeston.mmoc@gmail.com>2017-07-31 21:21:04 +0200
commit59db2eeea0a35028779fd76372ae06cc98c8086f (patch)
treea9532989868948cb309cb8d76a2e76a017fa9246 /src/server/game/AI
parentd24ce1739a799042d5a164794c09674227c8572c (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.h3
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp10
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h4
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp41
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.h1
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp86
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp6
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h21
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