Scripts/InstanceScript: Implement database framework for managing spawn groups based on boss state.
Port From (https://github.com/TrinityCore/TrinityCore/commit/608c9aaabfcdbaf09edd11cdf268c243b3e69478)
This commit is contained in:
@@ -113,7 +113,7 @@ namespace Framework.Constants
|
||||
AreaTrigger = 0x20,
|
||||
Conversation = 0x40,
|
||||
All = 0x7F,
|
||||
|
||||
|
||||
//GameObjects, Creatures(except pets), DynamicObject, Corpse(Bones), AreaTrigger
|
||||
AllGrid = GameObject | Creature | DynamicObject | Corpse | AreaTrigger | Conversation,
|
||||
|
||||
@@ -231,4 +231,13 @@ namespace Framework.Constants
|
||||
|
||||
All = (System | CompatibilityMode | ManualSpawn | DynamicSpawnRate | EscortQuestNpc)
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum InstanceSpawnGroupFlags
|
||||
{
|
||||
ActivateSpawn = 0x01,
|
||||
BlockSpawn = 0x02,
|
||||
|
||||
All = (ActivateSpawn | BlockSpawn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
using Framework.Collections;
|
||||
using Framework.Constants;
|
||||
using Framework.Database;
|
||||
using Framework.Dynamic;
|
||||
using Framework.GameMath;
|
||||
using Framework.IO;
|
||||
using Game.Conditions;
|
||||
using Game.DataStorage;
|
||||
using Game.Entities;
|
||||
@@ -31,8 +33,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Framework.Dynamic;
|
||||
using Framework.IO;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
@@ -5375,6 +5375,61 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, $"Loaded {numMembers} spawn group members in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
||||
}
|
||||
public void LoadInstanceSpawnGroups()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
|
||||
// 0 1 2 3 4
|
||||
SQLResult result = DB.World.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups");
|
||||
if (result.IsEmpty())
|
||||
{
|
||||
Log.outError(LogFilter.ServerLoading, "Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint count = 0;
|
||||
do
|
||||
{
|
||||
ushort instanceMapId = result.Read<ushort>(0);
|
||||
uint spawnGroupId = result.Read<uint>(3);
|
||||
|
||||
var spawnGroupTemplate = _spawnGroupDataStorage.LookupByKey(spawnGroupId);
|
||||
if (spawnGroupTemplate == null || spawnGroupTemplate.flags.HasAnyFlag(SpawnGroupFlags.System))
|
||||
{
|
||||
Log.outError(LogFilter.ServerLoading, $"Invalid spawn group {spawnGroupId} specified for instance {instanceMapId}. Skipped.");
|
||||
continue;
|
||||
}
|
||||
|
||||
InstanceSpawnGroupInfo info = new InstanceSpawnGroupInfo();
|
||||
info.SpawnGroupId = spawnGroupId;
|
||||
info.BossStateId = result.Read<byte>(1);
|
||||
|
||||
byte ALL_STATES = (1 << (int)EncounterState.ToBeDecided) - 1;
|
||||
byte states = result.Read<byte>(2);
|
||||
if ((states & ~ALL_STATES) != 0)
|
||||
{
|
||||
info.BossStates = (byte)(states & ALL_STATES);
|
||||
Log.outError(LogFilter.ServerLoading, $"Instance spawn group ({instanceMapId},{spawnGroupId}) had invalid boss state mask {states} - truncated to {info.BossStates}.");
|
||||
}
|
||||
else
|
||||
info.BossStates = states;
|
||||
|
||||
InstanceSpawnGroupFlags flags = (InstanceSpawnGroupFlags)result.Read<byte>(4);
|
||||
if ((flags & ~InstanceSpawnGroupFlags.All) != 0)
|
||||
{
|
||||
info.Flags = flags & InstanceSpawnGroupFlags.All;
|
||||
Log.outError(LogFilter.ServerLoading, $"Instance spawn group ({instanceMapId},{spawnGroupId}) had invalid flags {flags} - truncated to {info.Flags}.");
|
||||
}
|
||||
else
|
||||
info.Flags = flags;
|
||||
|
||||
_instanceSpawnGroupStorage.Add(instanceMapId, info);
|
||||
|
||||
++count;
|
||||
} while (result.NextRow());
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} instance spawn groups in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
||||
}
|
||||
void OnDeleteSpawnData(SpawnData data)
|
||||
{
|
||||
var templateIt = _spawnGroupDataStorage.LookupByKey(data.spawnGroupData.groupId);
|
||||
@@ -5394,24 +5449,24 @@ namespace Game
|
||||
|
||||
Cypher.Assert(false, $"Spawn data ({data.type},{data.spawnId}) being removed is member of spawn group {data.spawnGroupData.groupId}, but not actually listed in the lookup table for that group!");
|
||||
}
|
||||
public bool SpawnGroupSpawn(uint groupId, Map map, bool ignoreRespawn, bool force = false, List<WorldObject> spawnedObjects = null)
|
||||
public bool SpawnGroupSpawn(uint groupId, Map map, bool ignoreRespawn = false, bool force = false, List<WorldObject> spawnedObjects = null)
|
||||
{
|
||||
var spawnGroup = _spawnGroupDataStorage.LookupByKey(groupId);
|
||||
if (spawnGroup == null || spawnGroup.flags.HasAnyFlag(SpawnGroupFlags.System))
|
||||
{
|
||||
Log.outError(LogFilter.Maps, $"Tried to despawn non-existing (or system) spawn group {groupId}. Blocked.");
|
||||
Log.outError(LogFilter.Maps, $"Tried to spawn non-existing (or system) spawn group {groupId}. Blocked.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!map)
|
||||
{
|
||||
Log.outError(LogFilter.Maps, $"Tried to despawn creature group {groupId}, but no map was supplied. Blocked.");
|
||||
Log.outError(LogFilter.Maps, $"Tried to spawn creature group {groupId}, but no map was supplied. Blocked.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spawnGroup.mapId != map.GetId())
|
||||
{
|
||||
Log.outError(LogFilter.Maps, $"Tried to despawn creature group {groupId}, but supplied map is {map.GetId()}, creature group has map {spawnGroup.mapId}. Blocked.");
|
||||
Log.outError(LogFilter.Maps, $"Tried to spawn creature group {groupId}, but supplied map is {map.GetId()}, creature group has map {spawnGroup.mapId}. Blocked.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5543,13 +5598,14 @@ namespace Game
|
||||
return _dungeonEncounterStorage.LookupByKey(MathFunctions.MakePair64(mapId, (uint)difficulty));
|
||||
}
|
||||
public bool IsTransportMap(uint mapId) { return _transportMaps.Contains((ushort)mapId); }
|
||||
void SetSpawnGroupActive(uint groupId, bool state)
|
||||
SpawnGroupTemplateData GetSpawnGroupData(uint groupId) { return _spawnGroupDataStorage.LookupByKey(groupId); }
|
||||
public void SetSpawnGroupActive(uint groupId, bool state)
|
||||
{
|
||||
var spawnGroup = _spawnGroupDataStorage.LookupByKey(groupId);
|
||||
if (spawnGroup != null)
|
||||
spawnGroup.isActive = state;
|
||||
}
|
||||
bool IsSpawnGroupActive(uint groupId)
|
||||
public bool IsSpawnGroupActive(uint groupId)
|
||||
{
|
||||
var spawnGroup = _spawnGroupDataStorage.LookupByKey(groupId);
|
||||
return (spawnGroup != null) && spawnGroup.isActive;
|
||||
@@ -5567,6 +5623,7 @@ namespace Game
|
||||
|
||||
return null;
|
||||
}
|
||||
public List<InstanceSpawnGroupInfo> GetSpawnGroupsForInstance(uint instanceId) { return _instanceSpawnGroupStorage.LookupByKey(instanceId); }
|
||||
|
||||
//Player
|
||||
public void LoadPlayerInfo()
|
||||
@@ -10098,6 +10155,7 @@ namespace Game
|
||||
List<ushort> _transportMaps = new List<ushort>();
|
||||
Dictionary<uint, SpawnGroupTemplateData> _spawnGroupDataStorage = new Dictionary<uint, SpawnGroupTemplateData>();
|
||||
MultiMap<uint, SpawnData> _spawnGroupMapStorage = new MultiMap<uint, SpawnData>();
|
||||
MultiMap<ushort, InstanceSpawnGroupInfo> _instanceSpawnGroupStorage = new MultiMap<ushort, InstanceSpawnGroupInfo>();
|
||||
|
||||
//Spells /Skills / Phases
|
||||
Dictionary<uint, PhaseInfoStruct> _phaseInfoById = new Dictionary<uint, PhaseInfoStruct>();
|
||||
@@ -10512,6 +10570,14 @@ namespace Game
|
||||
public uint armor;
|
||||
}
|
||||
|
||||
public struct InstanceSpawnGroupInfo
|
||||
{
|
||||
public byte BossStateId;
|
||||
public byte BossStates;
|
||||
public uint SpawnGroupId;
|
||||
public InstanceSpawnGroupFlags Flags;
|
||||
}
|
||||
|
||||
public class SpellClickInfo
|
||||
{
|
||||
public uint spellId;
|
||||
|
||||
@@ -22,6 +22,7 @@ using Game.Entities;
|
||||
using Game.Groups;
|
||||
using Game.Networking.Packets;
|
||||
using Game.Scenarios;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
@@ -32,6 +33,7 @@ namespace Game.Maps
|
||||
public InstanceScript(InstanceMap map)
|
||||
{
|
||||
instance = map;
|
||||
_instanceSpawnGroups = Global.ObjectMgr.GetSpawnGroupsForInstance(map.GetId());
|
||||
}
|
||||
|
||||
public void SaveToDB()
|
||||
@@ -161,27 +163,6 @@ namespace Game.Maps
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMinionState(Creature minion, EncounterState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case EncounterState.NotStarted:
|
||||
if (!minion.IsAlive())
|
||||
minion.Respawn();
|
||||
else if (minion.IsInCombat())
|
||||
minion.GetAI().EnterEvadeMode();
|
||||
break;
|
||||
case EncounterState.InProgress:
|
||||
if (!minion.IsAlive())
|
||||
minion.Respawn();
|
||||
else if (minion.GetVictim() == null)
|
||||
minion.GetAI().DoZoneInCombat();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void UpdateDoorState(GameObject door)
|
||||
{
|
||||
var range = doors.LookupByKey(door.GetEntry());
|
||||
@@ -213,6 +194,65 @@ namespace Game.Maps
|
||||
door.SetGoState(open ? GameObjectState.Active : GameObjectState.Ready);
|
||||
}
|
||||
|
||||
void UpdateMinionState(Creature minion, EncounterState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case EncounterState.NotStarted:
|
||||
if (!minion.IsAlive())
|
||||
minion.Respawn();
|
||||
else if (minion.IsInCombat())
|
||||
minion.GetAI().EnterEvadeMode();
|
||||
break;
|
||||
case EncounterState.InProgress:
|
||||
if (!minion.IsAlive())
|
||||
minion.Respawn();
|
||||
else if (minion.GetVictim() == null)
|
||||
minion.GetAI().DoZoneInCombat();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
enum states { Block, Spawn, ForceBlock };
|
||||
void UpdateSpawnGroups()
|
||||
{
|
||||
if (_instanceSpawnGroups.Empty())
|
||||
return;
|
||||
|
||||
Dictionary<uint, states> newStates = new Dictionary<uint, states>();
|
||||
foreach (var info in _instanceSpawnGroups)
|
||||
{
|
||||
if (!newStates.ContainsKey(info.SpawnGroupId))
|
||||
newStates[info.SpawnGroupId] = 0;// makes sure there's a BLOCK value in the map
|
||||
|
||||
if (newStates[info.SpawnGroupId] == states.ForceBlock) // nothing will change this
|
||||
continue;
|
||||
|
||||
if (((1 << (int)GetBossState(info.BossStateId)) & info.BossStates) == 0)
|
||||
continue;
|
||||
|
||||
if (info.Flags.HasAnyFlag(InstanceSpawnGroupFlags.BlockSpawn))
|
||||
newStates[info.SpawnGroupId] = states.ForceBlock;
|
||||
else if (info.Flags.HasAnyFlag(InstanceSpawnGroupFlags.ActivateSpawn))
|
||||
newStates[info.SpawnGroupId] = states.Spawn;
|
||||
}
|
||||
|
||||
foreach (var pair in newStates)
|
||||
{
|
||||
uint groupId = pair.Key;
|
||||
bool doSpawn = pair.Value == states.Spawn;
|
||||
if (Global.ObjectMgr.IsSpawnGroupActive(groupId) == doSpawn)
|
||||
continue; // nothing to do here
|
||||
// if we should spawn group, then spawn it...
|
||||
if (doSpawn)
|
||||
Global.ObjectMgr.SpawnGroupSpawn(groupId, instance);
|
||||
else // otherwise, set it as inactive so it no longer respawns (but don't despawn it)
|
||||
Global.ObjectMgr.SetSpawnGroupActive(groupId, false);
|
||||
}
|
||||
}
|
||||
|
||||
public BossInfo GetBossInfo(uint id)
|
||||
{
|
||||
Cypher.Assert(id < bosses.Count);
|
||||
@@ -291,7 +331,7 @@ namespace Game.Maps
|
||||
if (bossInfo.state == EncounterState.ToBeDecided) // loading
|
||||
{
|
||||
bossInfo.state = state;
|
||||
//Log.outError(LogFilter.General "Inialize boss {0} state as {1}.", id, (uint32)state);
|
||||
Log.outDebug(LogFilter.Scripts, $"InstanceScript: Inialize boss {id} state as {state}.");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@@ -354,6 +394,7 @@ namespace Game.Maps
|
||||
UpdateMinionState(minion, state);
|
||||
}
|
||||
|
||||
UpdateSpawnGroups();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -364,6 +405,14 @@ namespace Game.Maps
|
||||
return player && player.GetSession().HasPermission(RBACPermissions.SkipCheckInstanceRequiredBosses);
|
||||
}
|
||||
|
||||
public virtual void Create()
|
||||
{
|
||||
for (uint i = 0; i < bosses.Count; ++i)
|
||||
SetBossState(i, EncounterState.NotStarted);
|
||||
|
||||
UpdateSpawnGroups();
|
||||
}
|
||||
|
||||
public virtual void Load(string data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data))
|
||||
@@ -411,6 +460,7 @@ namespace Game.Maps
|
||||
if (buff < EncounterState.ToBeDecided)
|
||||
SetBossState(pair.Key, buff);
|
||||
}
|
||||
UpdateSpawnGroups();
|
||||
}
|
||||
|
||||
public virtual string GetSaveData()
|
||||
@@ -852,6 +902,7 @@ namespace Game.Maps
|
||||
Dictionary<uint, uint> _gameObjectInfo = new Dictionary<uint, uint>();
|
||||
Dictionary<uint, ObjectGuid> _objectGuids = new Dictionary<uint, ObjectGuid>();
|
||||
uint completedEncounters;
|
||||
List<InstanceSpawnGroupInfo> _instanceSpawnGroups = new List<InstanceSpawnGroupInfo>();
|
||||
uint _entranceId;
|
||||
uint _temporaryEntranceId;
|
||||
uint _combatResurrectionTimer;
|
||||
|
||||
@@ -5173,6 +5173,8 @@ namespace Game.Maps
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
i_data.Create();
|
||||
}
|
||||
|
||||
public bool Reset(InstanceResetMethod method)
|
||||
|
||||
@@ -586,6 +586,9 @@ namespace Game
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Spawn Group Templates...");
|
||||
Global.ObjectMgr.LoadSpawnGroupTemplates();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading instance spawn groups...");
|
||||
Global.ObjectMgr.LoadInstanceSpawnGroups();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Data...");
|
||||
Global.ObjectMgr.LoadCreatures();
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
--
|
||||
DROP TABLE IF EXISTS `instance_spawn_groups`;
|
||||
CREATE TABLE `instance_spawn_groups` (
|
||||
`instanceMapId` smallint(5) unsigned not null,
|
||||
`bossStateId` tinyint(3) unsigned not null,
|
||||
`bossStates` tinyint(3) unsigned not null,
|
||||
`spawnGroupId` int(10) unsigned not null,
|
||||
`flags` tinyint(3) unsigned not null,
|
||||
PRIMARY KEY (`instanceMapId`,`bossStateId`,`spawnGroupId`,`bossStates`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
Reference in New Issue
Block a user