server-master/srcs/_plugins/WingsEmu.Plugins.BasicImplementation/Event/Miniland/AddObjMinilandEndLogicEventHandler.cs
2026-02-10 18:21:30 +01:00

253 lines
No EOL
9.5 KiB
C#

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using PhoenixLib.Events;
using WingsAPI.Game.Extensions.MinilandExtensions;
using WingsEmu.Game;
using WingsEmu.Game._i18n;
using WingsEmu.Game.Characters;
using WingsEmu.Game.Configurations.Miniland;
using WingsEmu.Game.Extensions;
using WingsEmu.Game.Extensions.Mates;
using WingsEmu.Game.Helpers;
using WingsEmu.Game.Helpers.Damages;
using WingsEmu.Game.Items;
using WingsEmu.Game.Maps;
using WingsEmu.Game.Mates;
using WingsEmu.Game.Miniland;
using WingsEmu.Game.Miniland.Events;
using WingsEmu.Game.Networking;
using WingsEmu.Packets.Enums;
namespace WingsEmu.Plugins.BasicImplementations.Event.Miniland;
public class AddObjMinilandEndLogicEventHandler : IAsyncEventProcessor<AddObjMinilandEndLogicEvent>
{
private readonly IGameLanguageService _languageService;
private readonly IMinilandManager _minilandManager;
private readonly IRandomGenerator _randomGenerator;
public AddObjMinilandEndLogicEventHandler(IMinilandManager minilandManager, IRandomGenerator randomGenerator, IGameLanguageService languageService)
{
_minilandManager = minilandManager;
_randomGenerator = randomGenerator;
_languageService = languageService;
}
public async Task HandleAsync(AddObjMinilandEndLogicEvent e, CancellationToken cancellation)
{
IMapInstance mapInstance = e.Miniland;
if (mapInstance.MapDesignObjects.Exists(x => x.InventorySlot == e.MapObject.InventoryItem.Slot))
{
return;
}
Game.Configurations.Miniland.Miniland minilandConfiguration = _minilandManager.GetMinilandConfiguration(mapInstance);
if (minilandConfiguration == default)
{
return;
}
bool isForced = false;
if (e.MapObject.InventoryItem.ItemInstance.GameItem.ItemType == ItemType.House)
{
ForcedPlacing forcedPlacing = minilandConfiguration.ForcedPlacings.FirstOrDefault(x =>
(int)x.SubType == e.MapObject.InventoryItem.ItemInstance.GameItem.ItemSubType);
if (forcedPlacing != default)
{
e.MapObject.MapX = forcedPlacing.ForcedLocation.X;
e.MapObject.MapY = forcedPlacing.ForcedLocation.Y;
}
isForced = true;
}
byte[] modifiedGrid = GetModifiedGrid(mapInstance, minilandConfiguration, e.MapObject, isForced);
if (!IsZoneConstructable(modifiedGrid, e.MapObject, mapInstance.Width, mapInstance.Height))
{
IGameItem gameItem = e.MapObject.InventoryItem.ItemInstance.GameItem;
e.Sender.SendDebugMessage($"[MINILAND] The zone is not constructable. Object Width: {gameItem.Width.ToString()} | Object Height: {gameItem.Height.ToString()}");
return;
}
MoveMatesColliding(e, minilandConfiguration);
mapInstance.MapDesignObjects.Add(e.MapObject);
if (e.MapObject.InventoryItem.ItemInstance.GameItem.IsWarehouse)
{
e.Sender.PlayerEntity.WareHouseSize = e.MapObject.InventoryItem.ItemInstance.GameItem.MinilandObjectPoint;
}
if (e.MapObject.InventoryItem.ItemInstance.GameItem.ItemType == ItemType.House)
{
_minilandManager.RelativeUpdateMinilandCapacity(e.Sender.PlayerEntity.Id, e.MapObject.InventoryItem.ItemInstance.GameItem.MinilandObjectPoint);
BroadcastCapacityUpdate(e);
}
mapInstance.Broadcast(e.MapObject.GenerateEffect(false));
mapInstance.Broadcast(e.MapObject.GenerateMinilandObject(false));
}
private void BroadcastCapacityUpdate(AddObjMinilandEndLogicEvent e)
{
foreach (IClientSession session in e.Miniland.Sessions)
{
if (session.PlayerEntity.Id == e.Sender.PlayerEntity.Id)
{
continue;
}
session.SendMinilandPublicInformation(_minilandManager, _languageService);
}
}
private void MoveMatesColliding(AddObjMinilandEndLogicEvent e, Game.Configurations.Miniland.Miniland minilandConfiguration)
{
(int x1, int y1, int x2, int y2) boundaries = GetMapObjectBoundaries(e.MapObject);
RestrictedZone secureZoneForMate = minilandConfiguration.RestrictedZones.FirstOrDefault(x =>
x.RestrictionTag == RestrictionType.OnlyMates);
int baseX1 = default;
int baseX2 = default;
int baseY1 = default;
int baseY2 = default;
if (secureZoneForMate != default)
{
(baseX1, baseY1, baseX2, baseY2) = GetNormalizedBoundaries(
secureZoneForMate.Corner1.X, secureZoneForMate.Corner1.Y, secureZoneForMate.Corner2.X, secureZoneForMate.Corner2.Y);
}
foreach (IMateEntity mateEntity in e.Miniland.GetAliveMates())
{
if (!IsInsideRectangle(boundaries, mateEntity.PositionX, mateEntity.PositionY))
{
continue;
}
if (secureZoneForMate != default && mateEntity.Owner.Id == e.Sender.PlayerEntity.Id)
{
mateEntity.ChangePosition(new Position((short)_randomGenerator.RandomNumber(baseX1, baseX2 + 1), (short)_randomGenerator.RandomNumber(baseY1, baseY2 + 1)));
e.Sender.BroadcastMateTeleport(mateEntity);
}
else
{
mateEntity.TeleportToCharacter();
}
}
}
private static byte[] GetModifiedGrid(IMapInstance mapInstance, Game.Configurations.Miniland.Miniland minilandConfiguration, MapDesignObject mapObject, bool forcedPlacing = false)
{
//TODO This should be reworked, it not needed to copy the map grid
byte[] mapGrid = forcedPlacing ? new byte[mapInstance.Width * mapInstance.Height] : mapInstance.Grid.ToArray();
foreach (MapDesignObject mapDesignObject in mapInstance.MapDesignObjects)
{
(int x1, int y1, int x2, int y2) = GetMapObjectBoundaries(mapDesignObject);
for (int i = x1; i <= x2; i++)
{
for (int j = y1; j <= y2; j++)
{
mapGrid[i + j * mapInstance.Width] = (byte)(mapGrid[i + j * mapInstance.Width] | 1);
}
}
}
foreach (IClientSession session in mapInstance.Sessions)
{
IPlayerEntity character = session.PlayerEntity;
mapGrid[character.PositionX + character.PositionY * mapInstance.Width] =
(byte)(mapGrid[character.PositionX + character.PositionY * mapInstance.Width] | 1);
}
foreach (RestrictedZone restrictedZone in minilandConfiguration.RestrictedZones)
{
bool blockZone = false;
switch (restrictedZone.RestrictionTag)
{
case RestrictionType.OnlySoilObjects when mapObject.InventoryItem.ItemInstance.GameItem.ItemType != ItemType.Minigame:
case RestrictionType.OnlyTerraceObjects when mapObject.InventoryItem.ItemInstance.GameItem.ItemType != ItemType.Terrace:
case RestrictionType.OnlyGardenObjects when mapObject.InventoryItem.ItemInstance.GameItem.ItemType != ItemType.Garden:
case RestrictionType.OnlyMates:
case RestrictionType.Unconstructable:
blockZone = true;
break;
}
if (!blockZone)
{
continue;
}
(int x1, int y1, int x2, int y2) = GetNormalizedBoundaries(
restrictedZone.Corner1.X, restrictedZone.Corner1.Y, restrictedZone.Corner2.X, restrictedZone.Corner2.Y);
for (int i = x1; i <= x2; i++)
{
for (int j = y1; j <= y2; j++)
{
mapGrid[i + j * mapInstance.Width] = (byte)(mapGrid[i + j * mapInstance.Width] | 1);
}
}
}
return mapGrid;
}
private static bool IsZoneConstructable(IReadOnlyList<byte> array, MapDesignObject mapObject, int width, int height)
{
(int x1, int y1, int x2, int y2) = GetMapObjectBoundaries(mapObject);
for (int i = x1; i <= x2; i++)
{
for (int j = y1; j <= y2; j++)
{
if (!array.IsWalkable(i, j, width, height))
{
return false;
}
}
}
return true;
}
private static (int x1, int y1, int x2, int y2) GetMapObjectBoundaries(MapDesignObject mapObject)
{
int x1 = mapObject.MapX;
int y1 = mapObject.MapY;
int x2 = mapObject.MapX + mapObject.InventoryItem.ItemInstance.GameItem.Width - 1;
int y2 = mapObject.MapY + mapObject.InventoryItem.ItemInstance.GameItem.Height - 1;
return GetNormalizedBoundaries(x1, y1, x2, y2);
}
private static (int x1, int y1, int x2, int y2) GetNormalizedBoundaries(int x1, int y1, int x2, int y2)
{
bool randBoolean1 = x1 < x2;
int baseX1 = randBoolean1 ? x1 : x2;
int baseX2 = randBoolean1 ? x2 : x1;
bool randBoolean2 = y1 < y2;
int baseY1 = randBoolean2 ? y1 : y2;
int baseY2 = randBoolean2 ? y2 : y1;
return (baseX1, baseY1, baseX2, baseY2);
}
private static bool IsInsideRectangle((int x1, int y1, int x2, int y2) boundaries, int mX, int mY)
{
(int x1, int y1, int x2, int y2) = boundaries;
return IsInsideRectangle(mX, mY, x1, y1, x2, y2);
}
private static bool IsInsideRectangle(int mX, int mY, int x1, int y1, int x2, int y2) =>
mX >= x1 && mX <= x2 &&
mY >= y1 && mY <= y2;
}