Skip to content

Commit 950a968

Browse files
Add MaxBuildingsPerLocation feature, which allows restricting players from building inside the loot locations in the area where zombies can walk and spawn
1 parent e955653 commit 950a968

File tree

6 files changed

+150
-17
lines changed

6 files changed

+150
-17
lines changed

BuildingRestrictions/BuildingRestrictions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<TargetFramework>net48</TargetFramework>
55
<LangVersion>latest</LangVersion>
66
<RootNamespace>RestoreMonarchy.BuildingRestrictions</RootNamespace>
7-
<Version>1.1.1</Version>
7+
<Version>1.2.0</Version>
88
</PropertyGroup>
99

1010
<ItemGroup>

BuildingRestrictions/BuildingRestrictionsConfiguration.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public class BuildingRestrictionsConfiguration : IRocketPluginConfiguration
2424
public bool EnableMaxStructureHeight { get; set; } = false;
2525
public float MaxStructureHeight { get; set; } = 100;
2626

27+
public bool EnableMaxBuildingsPerLocation { get; set; } = false;
28+
public float MaxBuildingsPerLocationHeight { get; set; } = 100;
29+
public int MaxBuildingsPerLocation { get; set; } = 10;
30+
2731
[XmlArrayItem("Barricade")]
2832
public BuildingRestriction[] Barricades { get; set; } = [];
2933
[XmlArrayItem("Structure")]
@@ -49,6 +53,10 @@ public void LoadDefaults()
4953
EnableMaxStructureHeight = false;
5054
MaxStructureHeight = 100;
5155

56+
EnableMaxBuildingsPerLocation = false;
57+
MaxBuildingsPerLocationHeight = 100;
58+
MaxBuildingsPerLocation = 10;
59+
5260
Barricades =
5361
[
5462
new("sentries", 0, EBuild.SENTRY.ToString(), null, 5),

BuildingRestrictions/BuildingRestrictionsPlugin.cs

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
using System;
1414
using System.Collections.Generic;
1515
using System.Diagnostics;
16+
using System.Drawing.Text;
1617
using System.Linq;
18+
using UnityEngine;
19+
using Logger = Rocket.Core.Logging.Logger;
1720

1821
namespace RestoreMonarchy.BuildingRestrictions
1922
{
@@ -78,7 +81,10 @@ protected override void Unload()
7881
{ "PlayerNotFound", "Player [[b]]{0}[[/b]] not found." },
7982
{ "BuildingStatsOtherNoPermission", "You don't have permission to check other player building stats." },
8083
{ "MaxBarricadeHeightRestriction", "You can't build [[b]]{0}[[/b]] because it's higher than max [[b]]{1}m[[/b]] height above the ground." },
81-
{ "MaxStructureHeightRestriction", "You can't build [[b]]{0}[[/b]] because it's higher than max [[b]]{1}m[[/b]] height above the ground." }
84+
{ "MaxStructureHeightRestriction", "You can't build [[b]]{0}[[/b]] because it's higher than max [[b]]{1}m[[/b]] height above the ground." },
85+
{ "BuildingsInLocationRestriction", "You can't build [[b]]{0}[[/b]] here because you have reached the limit of max [[b]]{1}[[/b]] buildings in this location." },
86+
{ "BuildingsInLocationRestrictionNone", "You can't build in this location." },
87+
{ "BuildingsInLocationRestrictionInfo", "You have built [[b]]{0}/{1}[[/b]] buildings in this location." }
8288
};
8389

8490
private void OnLevelLoaded(int level)
@@ -100,8 +106,11 @@ private void OnLevelLoaded(int level)
100106
{
101107
ItemId = drop.asset.id,
102108
Build = drop.asset.build,
103-
Transform = drop.model
109+
Transform = drop.model,
110+
IsInBound = TryGetBoundsWithHeight(drop.model.position, out byte boundIndex),
111+
BoundIndex = boundIndex
104112
};
113+
105114
BarricadeData data = drop.GetServersideData();
106115
Database.AddPlayerBarricade(data.owner, playerBarricade);
107116
}
@@ -119,8 +128,11 @@ private void OnLevelLoaded(int level)
119128
{
120129
ItemId = drop.asset.id,
121130
Construct = drop.asset.construct,
122-
Transform = drop.model
131+
Transform = drop.model,
132+
IsInBound = TryGetBoundsWithHeight(drop.model.position, out byte boundIndex),
133+
BoundIndex = boundIndex
123134
};
135+
124136
StructureData data = drop.GetServersideData();
125137
Database.AddPlayerStructure(data.owner, playerStructure);
126138
}
@@ -158,7 +170,7 @@ public decimal GetPlayerBuildingsMultiplier(IRocketPlayer player)
158170
return multiplier;
159171
}
160172

161-
private void OnDeployBarricadeRequested(Barricade barricade, ItemBarricadeAsset asset, UnityEngine.Transform hit, ref UnityEngine.Vector3 point, ref float angle_x, ref float angle_y, ref float angle_z, ref ulong owner, ref ulong group, ref bool shouldAllow)
173+
private void OnDeployBarricadeRequested(Barricade barricade, ItemBarricadeAsset asset, Transform hit, ref Vector3 point, ref float angle_x, ref float angle_y, ref float angle_z, ref ulong owner, ref ulong group, ref bool shouldAllow)
162174
{
163175
if (!shouldAllow)
164176
{
@@ -219,6 +231,34 @@ private void OnDeployBarricadeRequested(Barricade barricade, ItemBarricadeAsset
219231
}
220232
}
221233

234+
System.Action action = null;
235+
if (Configuration.Instance.EnableMaxBuildingsPerLocation && TryGetBoundsWithHeight(point, out byte boundIndex)
236+
&& asset.build != EBuild.BEACON && asset.build != EBuild.VEHICLE && asset.build != EBuild.CHARGE && asset.build != EBuild.SAFEZONE)
237+
{
238+
if (Configuration.Instance.MaxBuildingsPerLocation == 0)
239+
{
240+
SendMessageToPlayer(unturnedPlayer, "BuildingsInLocationRestrictionNone");
241+
shouldAllow = false;
242+
return;
243+
}
244+
245+
int barricadesCount = playerBuildings?.Barricades.Count(x => x.IsInBound && x.BoundIndex == boundIndex) ?? 0;
246+
int structuresCount = playerBuildings?.Structures.Count(x => x.IsInBound && x.BoundIndex == boundIndex) ?? 0;
247+
int buildingsCount = barricadesCount + structuresCount;
248+
249+
int maxBuildings = (int)(Configuration.Instance.MaxBuildingsPerLocation * multiplier);
250+
if (maxBuildings <= buildingsCount)
251+
{
252+
SendMessageToPlayer(unturnedPlayer, "BuildingsInLocationRestriction", itemName, maxBuildings);
253+
shouldAllow = false;
254+
return;
255+
}
256+
else
257+
{
258+
action = () => SendMessageToPlayer(unturnedPlayer, "BuildingsInLocationRestrictionInfo", buildingsCount + 1, maxBuildings);
259+
}
260+
}
261+
222262
if (Configuration.Instance.Barricades != null && Configuration.Instance.Barricades.Any())
223263
{
224264
int itemIdCount = playerBuildings?.Barricades.Count(x => x.ItemId == barricade.asset.id) ?? 0;
@@ -256,6 +296,11 @@ private void OnDeployBarricadeRequested(Barricade barricade, ItemBarricadeAsset
256296
return;
257297
}
258298
}
299+
300+
if (action != null)
301+
{
302+
action();
303+
}
259304
}
260305

261306
private void OnDeployStructureRequested(Structure structure, ItemStructureAsset asset, ref UnityEngine.Vector3 point, ref float angle_x, ref float angle_y, ref float angle_z, ref ulong owner, ref ulong group, ref bool shouldAllow)
@@ -319,6 +364,33 @@ private void OnDeployStructureRequested(Structure structure, ItemStructureAsset
319364
}
320365
}
321366

367+
System.Action action = null;
368+
if (Configuration.Instance.EnableMaxBuildingsPerLocation && TryGetBoundsWithHeight(point, out byte boundIndex))
369+
{
370+
if (Configuration.Instance.MaxBuildingsPerLocation == 0)
371+
{
372+
SendMessageToPlayer(unturnedPlayer, "BuildingsInLocationRestrictionNone");
373+
shouldAllow = false;
374+
return;
375+
}
376+
377+
int barricadesCount = playerBuildings?.Barricades.Count(x => x.IsInBound && x.BoundIndex == boundIndex) ?? 0;
378+
int structuresCount = playerBuildings?.Structures.Count(x => x.IsInBound && x.BoundIndex == boundIndex) ?? 0;
379+
int buildingsCount = barricadesCount + structuresCount;
380+
381+
int maxBuildings = (int)(Configuration.Instance.MaxBuildingsPerLocation * multiplier);
382+
if (maxBuildings <= buildingsCount)
383+
{
384+
SendMessageToPlayer(unturnedPlayer, "BuildingsInLocationRestriction", itemName, maxBuildings);
385+
shouldAllow = false;
386+
return;
387+
}
388+
else
389+
{
390+
action = () => SendMessageToPlayer(unturnedPlayer, "BuildingsInLocationRestrictionInfo", buildingsCount + 1, maxBuildings);
391+
}
392+
}
393+
322394
if (Configuration.Instance.Structures != null && Configuration.Instance.Structures.Any())
323395
{
324396
int itemIdCount = playerBuildings?.Structures.Count(x => x.ItemId == structure.asset.id) ?? 0;
@@ -356,6 +428,11 @@ private void OnDeployStructureRequested(Structure structure, ItemStructureAsset
356428
return;
357429
}
358430
}
431+
432+
if (action != null)
433+
{
434+
action();
435+
}
359436
}
360437

361438
private void OnBarricadeSpawned(BarricadeRegion region, BarricadeDrop drop)
@@ -365,7 +442,9 @@ private void OnBarricadeSpawned(BarricadeRegion region, BarricadeDrop drop)
365442
{
366443
ItemId = drop.asset.id,
367444
Build = drop.asset.build,
368-
Transform = drop.model
445+
Transform = drop.model,
446+
IsInBound = TryGetBoundsWithHeight(drop.model.position, out byte boundIndex),
447+
BoundIndex = boundIndex
369448
};
370449
Database.AddPlayerBarricade(barricadeData.owner, playerBarricade);
371450
}
@@ -377,11 +456,36 @@ private void OnStructureSpawned(StructureRegion region, StructureDrop drop)
377456
{
378457
ItemId = drop.asset.id,
379458
Construct = drop.asset.construct,
380-
Transform = drop.model
459+
Transform = drop.model,
460+
IsInBound = TryGetBoundsWithHeight(drop.model.position, out byte boundIndex),
461+
BoundIndex = boundIndex
381462
};
382463
Database.AddPlayerStructure(structureData.owner, playerStructure);
383464
}
384465

466+
public bool TryGetBoundsWithHeight(Vector3 position, out byte boundIndex)
467+
{
468+
if (LevelNavigation.tryGetBounds(position, out boundIndex))
469+
{
470+
Bounds bound = LevelNavigation.bounds[boundIndex];
471+
if (IsInBoundsHeight(bound, position))
472+
{
473+
return true;
474+
}
475+
}
476+
477+
return false;
478+
}
479+
480+
private bool IsInBoundsHeight(Bounds bound, Vector3 position)
481+
{
482+
float goundLevel = LevelGround.getHeight(position);
483+
float buildingY = position.y;
484+
float buildingHeight = buildingY - goundLevel;
485+
486+
return buildingHeight <= Configuration.Instance.MaxBuildingsPerLocationHeight;
487+
}
488+
385489
internal void SendMessageToPlayer(IRocketPlayer player, string translationKey, params object[] placeholder)
386490
{
387491
if (player == null)

BuildingRestrictions/Models/PlayerBarricade.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@ public class PlayerBarricade
88
public ushort ItemId { get; set; }
99
public EBuild Build { get; set; }
1010
public Transform Transform { get; set; }
11+
public bool IsInBound { get; set; }
12+
public byte BoundIndex { get; set; }
1113
}
1214
}

BuildingRestrictions/Models/PlayerStructure.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@ public class PlayerStructure
88
public ushort ItemId { get; set; }
99
public EConstruct Construct { get; set; }
1010
public Transform Transform { get; set; }
11+
public bool IsInBound { get; set; }
12+
public byte BoundIndex { get; set; }
1113
}
1214
}

README.md

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,32 @@ Set the limits for the number of buildings the player can build.
1111
<Permission Cooldown="0">buildingstats.other</Permission>
1212
```
1313

14-
## List of Barricade Build types
14+
## Barricade Build types
15+
```
1516
FORTIFICATION, BARRICADE, DOOR, GATE, BED, LADDER, STORAGE, FARM, TORCH, CAMPFIRE, SPIKE, WIRE, GENERATOR, SPOT, SAFEZONE, FREEFORM, SIGN, VEHICLE, CLAIM, BEACON, STORAGE_WALL, BARREL_RAIN, OIL, CAGE, SHUTTER, TANK, CHARGE, SENTRY, SENTRY_FREEFORM, OVEN, LIBRARY, OXYGENATOR, GLASS, NOTE, HATCH, MANNEQUIN, STEREO, SIGN_WALL, CLOCK, BARRICADE_WALL
17+
```
18+
19+
## Structure Construct types
20+
```
21+
FLOOR, WALL, RAMPART, ROOF, PILLAR, POST, FLOOR_POLY, ROOF_POLY
22+
```
23+
24+
## Location restrictions
25+
Location is considered a rectangle where the zombies can spawn and walk. It is the equalivent of navigations in Unturned map editor.
26+
```xml
27+
<EnableMaxBuildingsPerLocation>false</EnableMaxBuildingsPerLocation>
28+
<MaxBuildingsPerLocationHeight>100</MaxBuildingsPerLocationHeight>
29+
<MaxBuildingsPerLocation>10</MaxBuildingsPerLocation>
30+
```
31+
The `MaxBuildingsPerLocationHeight` is the height above the ground where the building is considered to be in the location.
32+
33+
If you want to completely disable building in the locations, set `MaxBuildingPerLocation` to `0`.
1634

17-
## List of Structure Construct types
18-
- FLOOR
19-
- WALL
20-
- RAMPART
21-
- ROOF
22-
- PILLAR
23-
- POST
24-
- FLOOR_POLY
25-
- ROOF_POLY
35+
The following barricades are not considered for the location restrictions:
36+
- Safezone Radiators
37+
- Horde Beacons
38+
- Charges
39+
- Vehicles (which can be built)
2640

2741
## Configuration
2842
```xml
@@ -41,6 +55,9 @@ FORTIFICATION, BARRICADE, DOOR, GATE, BED, LADDER, STORAGE, FARM, TORCH, CAMPFIR
4155
<MaxBarricadeHeight>100</MaxBarricadeHeight>
4256
<EnableMaxStructureHeight>false</EnableMaxStructureHeight>
4357
<MaxStructureHeight>100</MaxStructureHeight>
58+
<EnableMaxBuildingsPerLocation>false</EnableMaxBuildingsPerLocation>
59+
<MaxBuildingsPerLocationHeight>100</MaxBuildingsPerLocationHeight>
60+
<MaxBuildingsPerLocation>10</MaxBuildingsPerLocation>
4461
<Barricades>
4562
<Barricade Name="sentries" Build="SENTRY" Max="5" />
4663
<Barricade Name="stereos" Build="STEREO" Max="1" />

0 commit comments

Comments
 (0)