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:
hondacrx
2020-08-24 11:13:45 -04:00
parent cd92a34082
commit 24a9e5e1d8
6 changed files with 172 additions and 31 deletions
+10 -1
View File
@@ -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)
}
}
+74 -8
View File
@@ -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;
+73 -22
View File
@@ -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;
+2
View File
@@ -5173,6 +5173,8 @@ namespace Game.Maps
}
}
}
else
i_data.Create();
}
public bool Reset(InstanceResetMethod method)
+3
View File
@@ -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;