Skip to content

Commit 0779b8b

Browse files
authored
Merge pull request #51 from AlexBond2/blackout-zone
Blackout zone and Area Population
2 parents 747d797 + aeec403 commit 0779b8b

28 files changed

+739
-179
lines changed

src/MHServerEmu.Core/System/Random/GRandom.cs

+10
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,15 @@ public override string ToString()
8787
{
8888
return _rand.ToString();
8989
}
90+
91+
public void ShuffleList<T>(List<T> list)
92+
{
93+
if (list.Count > 1)
94+
for (int i = 0; i < list.Count; i++)
95+
{
96+
int j = Next(i, list.Count);
97+
(list[j], list[i]) = (list[i], list[j]);
98+
}
99+
}
90100
}
91101
}

src/MHServerEmu.Games/Entities/EntityManager.cs

+4-41
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
using MHServerEmu.Games.Entities.Locomotion;
66
using MHServerEmu.Games.GameData;
77
using MHServerEmu.Games.GameData.Prototypes;
8-
using MHServerEmu.Games.GameData.Prototypes.Markers;
9-
using MHServerEmu.Games.Generators.Population;
108
using MHServerEmu.Games.MetaGames;
119
using MHServerEmu.Games.Network;
1210
using MHServerEmu.Games.Properties;
@@ -69,7 +67,7 @@ public WorldEntity CreateWorldEntity(Cell cell, PrototypeId prototypeId, Propert
6967
else if (proto is TransitionPrototype transitionProto)
7068
worldEntity = new Transition(baseData, properties, Destination.FindDestination(cell, transitionProto));
7169
else
72-
worldEntity = new(baseData, AOINetworkPolicyValues.AOIChannelDiscovery, properties);
70+
worldEntity = new WorldEntity(baseData, AOINetworkPolicyValues.AOIChannelDiscovery, properties);
7371
worldEntity.RegionId = regionId;
7472
worldEntity.EnterWorld(cell, position, orientation);
7573
_entityDict.Add(baseData.EntityId, worldEntity);
@@ -78,16 +76,16 @@ public WorldEntity CreateWorldEntity(Cell cell, PrototypeId prototypeId, Propert
7876

7977
public WorldEntity CreateWorldEntityEmpty(ulong regionId, PrototypeId prototypeId, Vector3 position, Orientation orientation)
8078
{
81-
EntityBaseData baseData = new EntityBaseData(GetNextEntityId(), prototypeId, position, orientation);
82-
WorldEntity worldEntity = new(baseData, AOINetworkPolicyValues.AOIChannelProximity, new(_game.CurrentRepId));
79+
EntityBaseData baseData = new (GetNextEntityId(), prototypeId, position, orientation);
80+
WorldEntity worldEntity = new (baseData, AOINetworkPolicyValues.AOIChannelProximity, new(_game.CurrentRepId));
8381
worldEntity.RegionId = regionId;
8482
_entityDict.Add(baseData.EntityId, worldEntity);
8583
return worldEntity;
8684
}
8785

8886
public MetaGame CreateMetaGame(PrototypeId metaGameRef, ulong regionId)
8987
{
90-
EntityBaseData baseData = new EntityBaseData(GetNextEntityId(), metaGameRef, null, null);
88+
EntityBaseData baseData = new (GetNextEntityId(), metaGameRef, null, null);
9189
ReplicatedVariable<string> metaname = new(0, "");
9290
MetaGame metaGame = new(baseData, AOINetworkPolicyValues.AOIChannelProximity, new(_game.CurrentRepId), metaname)
9391
{
@@ -125,41 +123,6 @@ public Item CreateInvItem(PrototypeId itemProto, InventoryLocation invLoc, Proto
125123
return item;
126124
}
127125

128-
public Transition SpawnTargetTeleport(Cell cell, TransitionPrototype transitionProto, Vector3 position, Orientation orientation,
129-
bool requiresEnterGameWorld, PrototypeId targetRef, bool OverrideSnapToFloor)
130-
{
131-
if (cell == null) return default;
132-
Region region = cell.GetRegion();
133-
ulong regionId = region.Id;
134-
int mapAreaId = (int)cell.Area.Id;
135-
int mapCellId = (int)cell.Id;
136-
PrototypeId contextAreaRef = (PrototypeId)cell.Area.PrototypeId;
137-
138-
EntityBaseData baseData = (requiresEnterGameWorld == false)
139-
? new EntityBaseData(GetNextEntityId(), transitionProto.DataRef, position, orientation, OverrideSnapToFloor)
140-
: new EntityBaseData(GetNextEntityId(), transitionProto.DataRef, null, null);
141-
142-
Destination destination = null;
143-
144-
if (targetRef != PrototypeId.Invalid)
145-
destination = Destination.DestinationFromTarget(targetRef, region, transitionProto);
146-
147-
ReplicatedPropertyCollection properties = new(_game.CurrentRepId);
148-
properties[PropertyEnum.MapPosition] = position;
149-
properties[PropertyEnum.MapAreaId] = mapAreaId;
150-
properties[PropertyEnum.MapRegionId] = regionId;
151-
properties[PropertyEnum.MapCellId] = mapCellId;
152-
properties[PropertyEnum.ContextAreaRef] = contextAreaRef;
153-
154-
Transition transition = new(baseData, properties, destination);
155-
156-
transition.RegionId = regionId;
157-
transition.EnterWorld(cell, position, orientation);
158-
_entityDict.Add(baseData.EntityId, transition);
159-
160-
return transition;
161-
}
162-
163126
public void DestroyEntity(Entity entity)
164127
{
165128
// TODO

src/MHServerEmu.Games/Entities/RegionLocation.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,13 @@ public static Vector3 ProjectToFloor(Region region, Vector3 regionPos)
6666
Cell cell = region.GetCellAtPosition(regionPos);
6767
if (cell == null) return regionPos;
6868
Vector3 postion = new(regionPos);
69-
postion.Z = cell.RegionBounds.Center.Z + ProjectToFloor(cell.CellProto, postion);
69+
70+
var height = ProjectToFloor(cell.CellProto, postion);
71+
if (height > Int16.MinValue)
72+
postion.Z = cell.RegionBounds.Center.Z + height;
73+
else if (region.NaviMesh.IsMeshValid)
74+
return region.NaviMesh.ProjectToMesh(regionPos);
75+
7076
return postion;
7177
}
7278

src/MHServerEmu.Games/Entities/Spawner.cs

+11-2
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,18 @@ private void SpawnObject(PopulationObjectPrototype popObject)
8282
clusterGroup.Initialize();
8383
Vector3 pos = new(Location.GetPosition());
8484
var rot = Location.GetOrientation();
85-
clusterGroup.PickPositionInSector(pos, rot, spawnerProto.SpawnDistanceMin, spawnerProto.SpawnDistanceMax);
85+
if (spawnerProto.SpawnFailBehavior.HasFlag(SpawnFailBehavior.RetryIgnoringBlackout)
86+
|| spawnerProto.SpawnFailBehavior.HasFlag(SpawnFailBehavior.RetryForce))
87+
clusterGroup.SpawnFlags |= SpawnFlags.IgnoreBlackout;
88+
89+
bool success = clusterGroup.PickPositionInSector(pos, rot, spawnerProto.SpawnDistanceMin, spawnerProto.SpawnDistanceMax);
90+
if (success == false && spawnerProto.SpawnFailBehavior.HasFlag(SpawnFailBehavior.RetryForce))
91+
{
92+
clusterGroup.SetParentRelativePosition(pos);
93+
success = true;
94+
}
8695
// spawn Entity from Group
87-
clusterGroup.Spawn();
96+
if (success) clusterGroup.Spawn();
8897
}
8998
}
9099
}

src/MHServerEmu.Games/Entities/WorldEntity.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public virtual void EnterWorld(Cell cell, Vector3 position, Orientation orientat
142142
if (proto.ObjectiveInfo != null)
143143
TrackAfterDiscovery = proto.ObjectiveInfo.TrackAfterDiscovery;
144144
if (proto is HotspotPrototype) _flags |= EntityFlags.IsHotspot;
145-
145+
if (proto is AgentPrototype) cell.EnemySpawn();
146146
Location.Region = cell.GetRegion();
147147
Location.Cell = cell; // Set directly
148148
Location.SetPosition(position);

src/MHServerEmu.Games/Events/EventManager.cs

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using MHServerEmu.Games.Properties;
1212
using MHServerEmu.Games.Regions;
1313
using MHServerEmu.Core.VectorMath;
14+
using MHServerEmu.Frontend;
1415

1516
namespace MHServerEmu.Games.Events
1617
{
@@ -89,6 +90,7 @@ private void HandleEvent(GameEvent queuedEvent)
8990
case EventEnum.StartMagikUltimate: OnStartMagikUltimate(playerConnection, (NetStructPoint3)queuedEvent.Data); break;
9091
case EventEnum.EndMagikUltimate: OnEndMagikUltimate(playerConnection); break;
9192
case EventEnum.GetRegion: OnGetRegion(playerConnection, (Region)queuedEvent.Data); break;
93+
case EventEnum.ErrorInRegion: OnErrorInRegion(playerConnection, (PrototypeId)queuedEvent.Data); break;
9294
}
9395

9496
queuedEvent.IsRunning = false;
@@ -542,5 +544,11 @@ private void OnGetRegion(PlayerConnection playerConnection, Region region)
542544
foreach (IMessage message in messages)
543545
playerConnection.SendMessage(message);
544546
}
547+
548+
private void OnErrorInRegion(PlayerConnection playerConnection, PrototypeId regionProtoId)
549+
{
550+
Logger.Error($"Event ErrorInRegion {GameDatabase.GetFormattedPrototypeName(regionProtoId)}");
551+
playerConnection.Disconnect();
552+
}
545553
}
546554
}

src/MHServerEmu.Games/Events/GameEvent.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ public enum EventEnum
1818
PreInteractPower,
1919
PreInteractPowerEnd,
2020
UseInteractableObject,
21-
GetRegion
21+
GetRegion,
22+
ErrorInRegion
2223
}
2324

2425
public class GameEvent

src/MHServerEmu.Games/Game.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ private List<IMessage> GetBeginLoadingMessages(PlayerConnection playerConnection
252252
private void GetRegionAsync(PlayerConnection playerConnection)
253253
{
254254
Region region = RegionManager.GetRegion((RegionPrototypeId)playerConnection.RegionDataRef);
255-
EventManager.AddEvent(playerConnection, EventEnum.GetRegion, 0, region);
255+
if (region != null) EventManager.AddEvent(playerConnection, EventEnum.GetRegion, 0, region);
256+
else EventManager.AddEvent(playerConnection, EventEnum.ErrorInRegion, 0, playerConnection.RegionDataRef);
256257
}
257258

258259
private List<IMessage> GetFinishLoadingMessages(PlayerConnection playerConnection)

src/MHServerEmu.Games/GameData/Prototypes/DifficultyPrototype.cs

+14
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,20 @@ public class DifficultyTierPrototype : Prototype
117117
public AssetId UIColor { get; protected set; }
118118
public LocaleStringId UIDisplayName { get; protected set; }
119119
public int BonusItemFindBonusDifficultyMult { get; protected set; }
120+
121+
public static bool InRange(PrototypeId value, PrototypeId min, PrototypeId max)
122+
{
123+
var valueProto = GameDatabase.GetPrototype<DifficultyTierPrototype>(value);
124+
if (valueProto == null) return false;
125+
var minProto = GameDatabase.GetPrototype<DifficultyTierPrototype>(min);
126+
if (minProto == null || valueProto.Tier < minProto.Tier) return false;
127+
else
128+
{
129+
var maxProto = GameDatabase.GetPrototype<DifficultyTierPrototype>(max);
130+
if (maxProto == null || valueProto.Tier > maxProto.Tier) return false;
131+
}
132+
return true;
133+
}
120134
}
121135

122136
public class DifficultyGlobalsPrototype : Prototype

src/MHServerEmu.Games/GameData/Prototypes/PopulationObjectPrototype.cs

+33
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public override string ToString()
8686
return sb.ToString();
8787
}
8888

89+
public virtual float GetAverageSize() => 0.0f;
8990
}
9091

9192
public class PopulationEntityPrototype : PopulationObjectPrototype
@@ -110,6 +111,8 @@ public override void GetContainedEntities(HashSet<PrototypeId> entities, bool un
110111
entities.Add(Entity);
111112
}
112113

114+
public override float GetAverageSize() => 1.0f;
115+
113116
}
114117

115118
public class PopulationClusterFixedPrototype : PopulationObjectPrototype
@@ -148,6 +151,18 @@ public override void GetContainedEntities(HashSet<PrototypeId> entities, bool un
148151
InternalGetContainedEntities(entities, unwrapEntitySelectors);
149152
}
150153

154+
public override float GetAverageSize()
155+
{
156+
float count = 0.0f;
157+
if (Entities.HasValue())
158+
count += Entities.Length;
159+
160+
if (EntityEntries.HasValue())
161+
foreach (var entry in EntityEntries)
162+
count += entry.Count;
163+
return count;
164+
}
165+
151166
private void InternalGetContainedEntities(HashSet<PrototypeId> entities, bool unwrapEntitySelectors)
152167
{
153168
if (Entities.HasValue())
@@ -202,6 +217,8 @@ public override void GetContainedEntities(HashSet<PrototypeId> entities, bool un
202217
if (unwrapEntitySelectors == false || UnwrapEntitySelector(Entity, entities) == 0)
203218
entities.Add(Entity);
204219
}
220+
221+
public override float GetAverageSize() => (Min + Max) / 2.0f;
205222
}
206223

207224
public class PopulationClusterMixedPrototype : PopulationObjectPrototype
@@ -246,6 +263,8 @@ public override void GetContainedEntities(HashSet<PrototypeId> entities, bool un
246263
InternalGetContainedEntities(entities, unwrapEntitySelectors);
247264
}
248265

266+
public override float GetAverageSize() => (Min + Max) / 2.0f;
267+
249268
private void InternalGetContainedEntities(HashSet<PrototypeId> entities, bool unwrapEntitySelectors)
250269
{
251270
if (Choices.HasValue())
@@ -287,6 +306,18 @@ public override void GetContainedEntities(HashSet<PrototypeId> entities, bool un
287306
InternalGetContainedEntities(entities, unwrapEntitySelectors);
288307
}
289308

309+
public override float GetAverageSize()
310+
{
311+
float count = 0.0f;
312+
if (Henchmen.HasValue())
313+
{
314+
foreach (var henchmen in Henchmen)
315+
if (henchmen != null) count += henchmen.GetAverageSize();
316+
count /= Henchmen.Length;
317+
}
318+
return count + 1.0f;
319+
}
320+
290321
private void InternalGetContainedEntities(HashSet<PrototypeId> entities, bool unwrapEntitySelectors)
291322
{
292323
if (Leader != PrototypeId.Invalid)
@@ -366,6 +397,8 @@ private EncounterResourcePrototype GetEncounterResource()
366397
return GameDatabase.GetPrototype<EncounterResourcePrototype>(encounterProtoRef);
367398
}
368399

400+
public override float GetAverageSize() => 1.0f;
401+
369402
private PrototypeId GetEncounterRef()
370403
{
371404
if (EncounterResource == AssetId.Invalid)

src/MHServerEmu.Games/GameData/Prototypes/PopulationPrototype.cs

+17
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,23 @@ public class PopulationPrototype : Prototype
4747
public int SpawnMapDistributeDistance { get; protected set; }
4848
public int SpawnMapDistributeSpread { get; protected set; }
4949
public bool SpawnMapEnabled { get; protected set; }
50+
51+
public override void PostProcess()
52+
{
53+
base.PostProcess();
54+
55+
SpawnMapDensityMin = Math.Clamp(SpawnMapDensityMin, 0.0f, 0.8f);
56+
SpawnMapDensityMax = Math.Clamp(SpawnMapDensityMax, 0.0f, 0.8f);
57+
SpawnMapHeatBleed = Math.Clamp(SpawnMapHeatBleed, 0.0f, 0.8f);
58+
}
59+
60+
public float GetEncounterDensity(PrototypeId markerRef)
61+
{
62+
if (EncounterDensityOverrides.HasValue())
63+
foreach (var entry in EncounterDensityOverrides)
64+
if (entry.MarkerType == markerRef) return entry.Density;
65+
return EncounterDensityBase;
66+
}
5067
}
5168

5269
public class SpawnMarkerPrototype : Prototype

src/MHServerEmu.Games/Generators/EntityOctree.cs

-2
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,12 @@ public IEnumerable<WorldEntity> IterateElementsInVolume<B>(B bound, EntityRegion
216216
}
217217
}
218218

219-
// QuadtreeLocation<WorldEntity,EntityRegionSpatialPartitionElementOps<WorldEntity>,24>
220219
public class EntityRegionSpatialPartitionLocation : QuadtreeLocation<WorldEntity>
221220
{
222221
public EntityRegionSpatialPartitionLocation(WorldEntity element) : base(element) { }
223222
public override Aabb GetBounds() => Element.RegionBounds;
224223
}
225224

226-
// Quadtree<WorldEntity,EntityRegionSpatialPartitionElementOps<WorldEntity>,24>
227225
public class WorldEntityRegionSpatialPartition : Quadtree<WorldEntity>
228226
{
229227
public WorldEntityRegionSpatialPartition(Aabb bound, float minRadius, EntityRegionSPContextFlags flag) : base(bound, minRadius)

src/MHServerEmu.Games/Generators/Octree.cs

-38
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
using MHServerEmu.Core.Collisions;
22
using MHServerEmu.Core.VectorMath;
3-
using MHServerEmu.Games.Generators.Population;
4-
using MHServerEmu.Games.Regions;
53
using System.Collections;
64

75
namespace MHServerEmu.Games.Generators
86
{
9-
// Node
107
public class Node<T>
118
{
129
public Quadtree<T> Tree;
@@ -80,7 +77,6 @@ public bool IsEmpty()
8077
}
8178
}
8279

83-
// QuadtreeLocation
8480
public class QuadtreeLocation<T>
8581
{
8682
public T Element { get; }
@@ -98,21 +94,6 @@ public QuadtreeLocation(T element) {
9894
public virtual Aabb GetBounds() => default;
9995
}
10096

101-
// QuadtreeLocation<Cell,CellRegionSpatialPartitionElementOps<Cell>,24>
102-
public class CellRegionSpatialPartitionLocation : QuadtreeLocation<Cell>
103-
{
104-
public CellRegionSpatialPartitionLocation(Cell element) : base(element) { }
105-
public override Aabb GetBounds() => Element.RegionBounds;
106-
}
107-
108-
// QuadtreeLocation<SpawnReservation,SpawnReservationSpatialPartitionElementOps<SpawnReservation>,24>
109-
public class SpawnReservationSpatialPartitionLocation : QuadtreeLocation<SpawnReservation>
110-
{
111-
public SpawnReservationSpatialPartitionLocation(SpawnReservation element) : base(element) { }
112-
public override Aabb GetBounds() => Element.RegionBounds;
113-
}
114-
115-
// Quadtree
11697
public class Quadtree<T>
11798
{
11899
public Node<T> Root;
@@ -538,23 +519,4 @@ private void DecrementIteratorCount()
538519
private void IncrementIteratorCount() => _outstandingIteratorCount++;
539520

540521
}
541-
542-
// Quadtree<SpawnReservation,SpawnReservationSpatialPartitionElementOps<SpawnReservation>,24>
543-
public class SpawnReservationSpatialPartition : Quadtree<SpawnReservation>
544-
{
545-
public SpawnReservationSpatialPartition(Aabb bound): base (bound, 128.0f) { }
546-
547-
public override QuadtreeLocation<SpawnReservation> GetLocation(SpawnReservation element) => element.SpatialPartitionLocation;
548-
public override Aabb GetElementBounds(SpawnReservation element) => element.RegionBounds;
549-
}
550-
551-
// Quadtree<Cell,CellRegionSpatialPartitionElementOps<Cell>,24>
552-
public class CellSpatialPartition : Quadtree<Cell>
553-
{
554-
public CellSpatialPartition(Aabb bound) : base(bound, 128.0f) { }
555-
556-
public override QuadtreeLocation<Cell> GetLocation(Cell element) => element.SpatialPartitionLocation;
557-
public override Aabb GetElementBounds(Cell element) => element.RegionBounds;
558-
}
559-
560522
}

0 commit comments

Comments
 (0)