2023-10-15 11:20:49 -07:00
|
|
|
/*************************************************************************
|
|
|
|
|
* ModernUO *
|
|
|
|
|
* Copyright 2019-2023 - ModernUO Development Team *
|
|
|
|
|
* Email: hi@modernuo.com *
|
|
|
|
|
* File: Map.cs *
|
|
|
|
|
* *
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify *
|
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or *
|
|
|
|
|
* (at your option) any later version. *
|
|
|
|
|
* *
|
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
|
|
|
|
*************************************************************************/
|
|
|
|
|
|
2020-08-25 18:53:35 -07:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2021-04-23 18:57:42 -07:00
|
|
|
using System.Diagnostics;
|
2022-03-20 23:15:59 -07:00
|
|
|
using System.Runtime.CompilerServices;
|
2022-03-22 20:07:32 -07:00
|
|
|
using Server.Buffers;
|
2023-10-15 11:20:49 -07:00
|
|
|
using Server.Collections;
|
2020-08-25 18:53:35 -07:00
|
|
|
using Server.Items;
|
2021-04-23 18:57:42 -07:00
|
|
|
using Server.Logging;
|
2020-08-25 18:53:35 -07:00
|
|
|
using Server.Network;
|
|
|
|
|
using Server.Targeting;
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
namespace Server;
|
|
|
|
|
|
|
|
|
|
[Flags]
|
|
|
|
|
public enum MapRules
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
None = 0x0000,
|
|
|
|
|
Internal = 0x0001, // Internal map (used for dragging, commodity deeds, etc)
|
|
|
|
|
FreeMovement = 0x0002, // Anyone can move over anyone else without taking stamina loss
|
|
|
|
|
BeneficialRestrictions = 0x0004, // Disallow performing beneficial actions on criminals/murderers
|
|
|
|
|
HarmfulRestrictions = 0x0008, // Disallow performing harmful actions on innocents
|
|
|
|
|
TrammelRules = FreeMovement | BeneficialRestrictions | HarmfulRestrictions,
|
|
|
|
|
FeluccaRules = None
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
|
|
|
|
public const int SectorSize = 16;
|
|
|
|
|
public const int SectorShift = 4;
|
|
|
|
|
public const int SectorActiveRange = 2;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-06-05 01:00:22 -07:00
|
|
|
private static ILogger logger = LogFactory.GetLogger(typeof(Map));
|
2022-03-22 20:07:32 -07:00
|
|
|
private readonly int m_FileIndex;
|
|
|
|
|
private readonly Sector[][] m_Sectors;
|
|
|
|
|
private readonly int m_SectorsHeight;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
private readonly int m_SectorsWidth;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
private Region m_DefaultRegion;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
private string m_Name;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
private TileMatrix m_Tiles;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Map(int mapID, int mapIndex, int fileIndex, int width, int height, int season, string name, MapRules rules)
|
|
|
|
|
{
|
|
|
|
|
MapID = mapID;
|
|
|
|
|
MapIndex = mapIndex;
|
|
|
|
|
m_FileIndex = fileIndex;
|
|
|
|
|
Width = width;
|
|
|
|
|
Height = height;
|
|
|
|
|
Season = season;
|
|
|
|
|
m_Name = name;
|
|
|
|
|
Rules = rules;
|
|
|
|
|
Regions = new Dictionary<string, Region>(StringComparer.OrdinalIgnoreCase);
|
2023-11-05 08:17:34 -08:00
|
|
|
_invalidSector = new Sector(0, 0, this);
|
2022-03-22 20:07:32 -07:00
|
|
|
m_SectorsWidth = width >> SectorShift;
|
|
|
|
|
m_SectorsHeight = height >> SectorShift;
|
|
|
|
|
m_Sectors = new Sector[m_SectorsWidth][];
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public static Map[] Maps { get; } = new Map[0x100];
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public static Map Felucca => Maps[0];
|
|
|
|
|
public static Map Trammel => Maps[1];
|
|
|
|
|
public static Map Ilshenar => Maps[2];
|
|
|
|
|
public static Map Malas => Maps[3];
|
|
|
|
|
public static Map Tokuno => Maps[4];
|
|
|
|
|
public static Map TerMur => Maps[5];
|
|
|
|
|
public static Map Internal => Maps[0x7F];
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public static List<Map> AllMaps { get; } = new();
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public int Season { get; set; }
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-26 09:39:46 -08:00
|
|
|
public TileMatrix Tiles => m_Tiles ??= new TileMatrix(this, m_FileIndex, MapID, Width, Height);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public int MapID { get; }
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public int MapIndex { get; }
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public int Width { get; }
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public int Height { get; }
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Dictionary<string, Region> Regions { get; }
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Region DefaultRegion
|
|
|
|
|
{
|
|
|
|
|
get => m_DefaultRegion ??= new Region(null, this, 0, Array.Empty<Rectangle3D>());
|
|
|
|
|
set => m_DefaultRegion = value;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public MapRules Rules { get; set; }
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-05 08:17:34 -08:00
|
|
|
private readonly Sector _invalidSector;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public string Name
|
|
|
|
|
{
|
|
|
|
|
get
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
if (this == Internal && m_Name != "Internal")
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-11-13 00:36:23 -08:00
|
|
|
logger.Warning(
|
|
|
|
|
$"Internal map name was '{{Name}}'{Environment.NewLine}{{StackTrace}}",
|
|
|
|
|
m_Name,
|
|
|
|
|
new StackTrace()
|
|
|
|
|
);
|
2022-03-22 20:07:32 -07:00
|
|
|
m_Name = "Internal";
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return m_Name;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (this == Internal && value != "Internal")
|
|
|
|
|
{
|
2022-11-13 00:36:23 -08:00
|
|
|
logger.Warning(
|
|
|
|
|
$"Attempted to set internal map name to '{{Value}}'{Environment.NewLine}{{StackTrace}}",
|
|
|
|
|
value,
|
|
|
|
|
new StackTrace()
|
|
|
|
|
);
|
2022-03-22 20:07:32 -07:00
|
|
|
value = "Internal";
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
|
|
|
|
|
m_Name = value;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public static int[] InvalidLandTiles { get; set; } = { 0x244 };
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public static int MaxLOSDistance { get; set; } = 25;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public int CompareTo(Map other) => other == null ? -1 : MapID.CompareTo(other.MapID);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public static string[] GetMapNames()
|
|
|
|
|
{
|
|
|
|
|
var mapCount = 0;
|
|
|
|
|
for (var i = 0; i < Maps.Length; i++)
|
2021-12-28 14:32:45 -08:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var map = Maps[i];
|
|
|
|
|
if (map != null)
|
2021-12-28 14:32:45 -08:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
mapCount++;
|
2021-12-28 14:32:45 -08:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var mapNames = new string[mapCount];
|
|
|
|
|
for (int i = 0, mIndex = 0; i < Maps.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var map = Maps[i];
|
|
|
|
|
if (map != null)
|
2021-12-28 14:32:45 -08:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
mapNames[mIndex++] = map.Name;
|
2021-12-28 14:32:45 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return mapNames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Map[] GetMapValues()
|
|
|
|
|
{
|
|
|
|
|
var mapCount = 0;
|
|
|
|
|
for (var i = 0; i < Maps.Length; i++)
|
2021-12-28 14:32:45 -08:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var map = Maps[i];
|
|
|
|
|
if (map != null)
|
2021-12-28 14:32:45 -08:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
mapCount++;
|
2021-12-28 14:32:45 -08:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2021-12-28 14:32:45 -08:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var mapValues = new Map[mapCount];
|
|
|
|
|
for (int i = 0, mIndex = 0; i < Maps.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var map = Maps[i];
|
|
|
|
|
if (map != null)
|
2021-12-28 14:32:45 -08:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
mapValues[mIndex++] = map;
|
2021-12-28 14:32:45 -08:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mapValues;
|
|
|
|
|
}
|
2021-12-28 14:32:45 -08:00
|
|
|
|
2022-11-06 17:37:30 -08:00
|
|
|
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider)
|
|
|
|
|
{
|
|
|
|
|
if (destination.Length >= Name.Length)
|
|
|
|
|
{
|
|
|
|
|
Name.CopyTo(destination);
|
|
|
|
|
charsWritten = Name.Length;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
charsWritten = 0;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public override string ToString() => Name;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-11-06 17:37:30 -08:00
|
|
|
public string ToString(string format, IFormatProvider formatProvider)
|
|
|
|
|
{
|
|
|
|
|
// format and formatProvider are not doing anything right now, so use the
|
|
|
|
|
// default ToString implementation.
|
|
|
|
|
return ToString();
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public int GetAverageZ(int x, int y)
|
|
|
|
|
{
|
|
|
|
|
GetAverageZ(x, y, out _, out var avg, out _);
|
|
|
|
|
return avg;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public void GetAverageZ(int x, int y, out int z, out int avg, out int top)
|
|
|
|
|
{
|
|
|
|
|
var zTop = Tiles.GetLandTile(x, y).Z;
|
|
|
|
|
var zLeft = Tiles.GetLandTile(x, y + 1).Z;
|
|
|
|
|
var zRight = Tiles.GetLandTile(x + 1, y).Z;
|
|
|
|
|
var zBottom = Tiles.GetLandTile(x + 1, y + 1).Z;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
z = zTop;
|
|
|
|
|
if (zLeft < z)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
z = zLeft;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (zRight < z)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
z = zRight;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (zBottom < z)
|
|
|
|
|
{
|
|
|
|
|
z = zBottom;
|
|
|
|
|
}
|
2020-09-12 15:31:21 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
top = zTop;
|
|
|
|
|
if (zLeft > top)
|
|
|
|
|
{
|
|
|
|
|
top = zLeft;
|
|
|
|
|
}
|
2020-09-12 15:31:21 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (zRight > top)
|
|
|
|
|
{
|
|
|
|
|
top = zRight;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (zBottom > top)
|
|
|
|
|
{
|
|
|
|
|
top = zBottom;
|
|
|
|
|
}
|
2020-09-12 15:31:21 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
avg = (zTop - zBottom).Abs() > (zLeft - zRight).Abs()
|
|
|
|
|
? FloorAverage(zLeft, zRight)
|
|
|
|
|
: FloorAverage(zTop, zBottom);
|
|
|
|
|
}
|
2020-09-12 15:31:21 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
private static int FloorAverage(int a, int b)
|
|
|
|
|
{
|
|
|
|
|
var v = a + b;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (v < 0)
|
|
|
|
|
{
|
|
|
|
|
--v;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return v / 2;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
private static void AcquireFixItems(Map map, int x, int y, Item[] pool, out int length)
|
|
|
|
|
{
|
|
|
|
|
length = 0;
|
|
|
|
|
if (map == null || map == Internal || x < 0 || x > map.Width || y < 0 || y > map.Height)
|
|
|
|
|
{
|
|
|
|
|
return;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2024-05-09 14:01:25 -07:00
|
|
|
foreach (var item in map.GetItemsAt(x, y))
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
if (length == 128)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
break;
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
|
|
|
|
|
pool[length++] = item;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
Array.Sort(pool, 0, length, ZComparer.Default);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void FixColumn(int x, int y)
|
|
|
|
|
{
|
|
|
|
|
var landTile = Tiles.GetLandTile(x, y);
|
|
|
|
|
|
|
|
|
|
GetAverageZ(x, y, out _, out var landAvg, out _);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var items = STArrayPool<Item>.Shared.Rent(128);
|
|
|
|
|
AcquireFixItems(this, x, y, items, out var length);
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < length; i++)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var toFix = items[i];
|
|
|
|
|
|
|
|
|
|
if (!toFix.Movable)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var z = int.MinValue;
|
|
|
|
|
var currentZ = toFix.Z;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (!landTile.Ignored && landAvg <= currentZ)
|
|
|
|
|
{
|
|
|
|
|
z = landAvg;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-26 09:39:46 -08:00
|
|
|
foreach (var tile in Tiles.GetStaticAndMultiTiles(x, y))
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
|
|
|
|
|
|
|
|
|
var checkZ = tile.Z;
|
|
|
|
|
var checkTop = checkZ + id.CalcHeight;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (checkTop == checkZ && !id.Surface)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
++checkTop;
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (checkTop > z && checkTop <= currentZ)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
z = checkTop;
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
for (var j = 0; j < length; ++j)
|
|
|
|
|
{
|
|
|
|
|
if (j == i)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
continue;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var item = items[j];
|
|
|
|
|
var id = item.ItemData;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var checkZ = item.Z;
|
|
|
|
|
var checkTop = checkZ + id.CalcHeight;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (checkTop == checkZ && !id.Surface)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
++checkTop;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (checkTop > z && checkTop <= currentZ)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
z = checkTop;
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (z != int.MinValue)
|
|
|
|
|
{
|
|
|
|
|
toFix.Location = new Point3D(toFix.X, toFix.Y, z);
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
STArrayPool<Item>.Shared.Return(items, true);
|
|
|
|
|
}
|
2020-09-12 15:31:21 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the highest surface that is lower than <paramref name="p" />.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="p">The reference point.</param>
|
|
|
|
|
/// <returns>A surface <typeparamref name="Tile" /> or <typeparamref name="Item" />.</returns>
|
|
|
|
|
public object GetTopSurface(Point3D p)
|
|
|
|
|
{
|
|
|
|
|
if (this == Internal)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
object surface = null;
|
|
|
|
|
var surfaceZ = int.MinValue;
|
|
|
|
|
|
|
|
|
|
var lt = Tiles.GetLandTile(p.X, p.Y);
|
|
|
|
|
|
|
|
|
|
if (!lt.Ignored)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var avgZ = GetAverageZ(p.X, p.Y);
|
|
|
|
|
|
|
|
|
|
if (avgZ <= p.Z)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
surface = lt;
|
|
|
|
|
surfaceZ = avgZ;
|
|
|
|
|
|
|
|
|
|
if (surfaceZ == p.Z)
|
|
|
|
|
{
|
|
|
|
|
return surface;
|
|
|
|
|
}
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-26 09:39:46 -08:00
|
|
|
foreach (var tile in Tiles.GetStaticAndMultiTiles(p.X, p.Y))
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
|
|
|
|
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (id.Surface || id.Wet)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var tileZ = tile.Z + id.CalcHeight;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (tileZ > surfaceZ && tileZ <= p.Z)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
surface = tile;
|
|
|
|
|
surfaceZ = tileZ;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
|
|
|
|
if (surfaceZ == p.Z)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2020-08-25 18:53:35 -07:00
|
|
|
return surface;
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sector = GetSector(p.X, p.Y);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
foreach (var item in sector.Items)
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
if (item is BaseMulti || item.ItemID > TileData.MaxItemValue || !item.AtWorldPoint(p.X, p.Y) ||
|
|
|
|
|
item.Movable)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var id = item.ItemData;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
if (id.Surface || id.Wet)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
var itemZ = item.Z + id.CalcHeight;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
if (itemZ > surfaceZ && itemZ <= p.Z)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
surface = item;
|
|
|
|
|
surfaceZ = itemZ;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
if (surfaceZ == p.Z)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
return surface;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return surface;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public void Bound(int x, int y, out int newX, out int newY)
|
|
|
|
|
{
|
|
|
|
|
newX = Math.Clamp(x, 0, Width - 1);
|
|
|
|
|
newY = Math.Clamp(y, 0, Height - 1);
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Point2D Bound(Point3D p)
|
|
|
|
|
{
|
|
|
|
|
Bound(p.m_X, p.m_Y, out var x, out var y);
|
|
|
|
|
return new Point2D(x, y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Point2D Bound(Point2D p)
|
|
|
|
|
{
|
|
|
|
|
Bound(p.m_X, p.m_Y, out var x, out var y);
|
|
|
|
|
return new Point2D(x, y);
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-21 14:20:16 -07:00
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
2023-10-16 20:51:02 -07:00
|
|
|
private void CalculateSectors(
|
|
|
|
|
Rectangle2D bounds,
|
|
|
|
|
out int sectorStartX, out int sectorStartY,
|
|
|
|
|
out int sectorEndX, out int sectorEndY)
|
|
|
|
|
{
|
|
|
|
|
int left = bounds.Start.X;
|
|
|
|
|
int top = bounds.Start.Y;
|
|
|
|
|
int right = bounds.End.X;
|
|
|
|
|
int bottom = bounds.End.Y;
|
|
|
|
|
|
|
|
|
|
// Limit the coordinates to inside the valid map region
|
|
|
|
|
Bound(left, top, out left, out top);
|
|
|
|
|
Bound(right - 1, bottom - 1, out right, out bottom);
|
|
|
|
|
|
|
|
|
|
// Calculate the top left sector
|
|
|
|
|
sectorStartX = left >> SectorShift;
|
|
|
|
|
sectorStartY = top >> SectorShift;
|
|
|
|
|
|
|
|
|
|
// Calculate the bottom right sector.
|
|
|
|
|
sectorEndX = right >> SectorShift;
|
|
|
|
|
sectorEndY = bottom >> SectorShift;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public void ActivateSectors(int cx, int cy)
|
|
|
|
|
{
|
|
|
|
|
for (var x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
for (var y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var sect = GetRealSector(x, y);
|
2023-11-05 08:17:34 -08:00
|
|
|
if (sect != _invalidSector)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
sect.Activate();
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public void DeactivateSectors(int cx, int cy)
|
|
|
|
|
{
|
|
|
|
|
for (var x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
for (var y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var sect = GetRealSector(x, y);
|
2023-11-05 08:17:34 -08:00
|
|
|
if (sect != _invalidSector && !PlayersInRange(sect, SectorActiveRange))
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
sect.Deactivate();
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
private bool PlayersInRange(Sector sect, int range)
|
|
|
|
|
{
|
|
|
|
|
for (var x = sect.X - range; x <= sect.X + range; ++x)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
for (var y = sect.Y - range; y <= sect.Y + range; ++y)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var check = GetRealSector(x, y);
|
2023-11-05 08:17:34 -08:00
|
|
|
if (check != _invalidSector && check.Clients.Count > 0)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return true;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnClientChange(NetState oldState, NetState newState, Mobile m)
|
|
|
|
|
{
|
|
|
|
|
if (this != Internal)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
GetSector(m.Location).OnClientChange(oldState, newState);
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2024-01-23 23:08:52 -08:00
|
|
|
internal void OnEnter(Mobile m)
|
2023-10-29 22:42:46 -07:00
|
|
|
{
|
|
|
|
|
OnEnter(m.Location, m);
|
|
|
|
|
}
|
2023-10-16 20:51:02 -07:00
|
|
|
|
2024-01-23 23:08:52 -08:00
|
|
|
internal void OnEnter(Point3D p, Mobile m)
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
|
|
|
|
if (this != Internal)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
GetSector(p).OnEnter(m);
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2024-01-23 23:08:52 -08:00
|
|
|
internal void OnEnter(Item item)
|
2023-10-16 20:51:02 -07:00
|
|
|
{
|
|
|
|
|
OnEnter(item.Location, item);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-23 23:08:52 -08:00
|
|
|
internal void OnEnter(Point3D p, Item item)
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
2024-01-23 23:08:52 -08:00
|
|
|
if (this == Internal || item.Parent != null)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
GetSector(p).OnEnter(item);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (item is BaseMulti m)
|
|
|
|
|
{
|
|
|
|
|
var mcl = m.Components;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var start = GetMultiMinSector(m.Location, mcl);
|
|
|
|
|
var end = GetMultiMaxSector(m.Location, mcl);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
AddMulti(m, start, end);
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2024-01-23 23:08:52 -08:00
|
|
|
internal void OnLeave(Mobile m)
|
2023-10-29 22:42:46 -07:00
|
|
|
{
|
|
|
|
|
OnLeave(m.Location, m);
|
|
|
|
|
}
|
2023-10-16 20:51:02 -07:00
|
|
|
|
2024-01-23 23:08:52 -08:00
|
|
|
internal void OnLeave(Point3D p, Mobile m)
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
|
|
|
|
if (this != Internal)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
GetSector(p).OnLeave(m);
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2024-01-23 23:08:52 -08:00
|
|
|
internal void OnLeave(Item item)
|
2023-10-16 20:51:02 -07:00
|
|
|
{
|
|
|
|
|
OnLeave(item.Location, item);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-23 23:08:52 -08:00
|
|
|
internal void OnLeave(Point3D p, Item item)
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
2024-01-23 23:08:52 -08:00
|
|
|
if (this == Internal || item.Parent != null)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
GetSector(p).OnLeave(item);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (item is BaseMulti m)
|
|
|
|
|
{
|
|
|
|
|
var mcl = m.Components;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var start = GetMultiMinSector(m.Location, mcl);
|
|
|
|
|
var end = GetMultiMaxSector(m.Location, mcl);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
RemoveMulti(m, start, end);
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-05 08:17:34 -08:00
|
|
|
private void RemoveMulti(BaseMulti m, Sector start, Sector end)
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
|
|
|
|
if (this == Internal)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
for (var x = start.X; x <= end.X; ++x)
|
|
|
|
|
{
|
|
|
|
|
for (var y = start.Y; y <= end.Y; ++y)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
InternalGetSector(x, y).OnMultiLeave(m);
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-05 08:17:34 -08:00
|
|
|
private void AddMulti(BaseMulti m, Sector start, Sector end)
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
|
|
|
|
if (this == Internal)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
for (var x = start.X; x <= end.X; ++x)
|
|
|
|
|
{
|
|
|
|
|
for (var y = start.Y; y <= end.Y; ++y)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
InternalGetSector(x, y).OnMultiEnter(m);
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Sector GetMultiMinSector(Point3D loc, MultiComponentList mcl) =>
|
|
|
|
|
GetSector(Bound(new Point2D(loc.m_X + mcl.Min.m_X, loc.m_Y + mcl.Min.m_Y)));
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Sector GetMultiMaxSector(Point3D loc, MultiComponentList mcl) =>
|
|
|
|
|
GetSector(Bound(new Point2D(loc.m_X + mcl.Max.m_X, loc.m_Y + mcl.Max.m_Y)));
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public void OnMove(Point3D oldLocation, Mobile m)
|
|
|
|
|
{
|
|
|
|
|
if (this == Internal)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var oldSector = GetSector(oldLocation);
|
|
|
|
|
var newSector = GetSector(m.Location);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (oldSector != newSector)
|
|
|
|
|
{
|
|
|
|
|
oldSector.OnLeave(m);
|
|
|
|
|
newSector.OnEnter(m);
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public void OnMove(Point3D oldLocation, Item item)
|
|
|
|
|
{
|
|
|
|
|
if (this == Internal)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var oldSector = GetSector(oldLocation);
|
|
|
|
|
var newSector = GetSector(item.Location);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (oldSector != newSector)
|
|
|
|
|
{
|
|
|
|
|
oldSector.OnLeave(item);
|
|
|
|
|
newSector.OnEnter(item);
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (item is BaseMulti m)
|
|
|
|
|
{
|
|
|
|
|
var mcl = m.Components;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var start = GetMultiMinSector(m.Location, mcl);
|
|
|
|
|
var end = GetMultiMaxSector(m.Location, mcl);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var oldStart = GetMultiMinSector(oldLocation, mcl);
|
|
|
|
|
var oldEnd = GetMultiMaxSector(oldLocation, mcl);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (oldStart != start || oldEnd != end)
|
|
|
|
|
{
|
|
|
|
|
RemoveMulti(m, oldStart, oldEnd);
|
|
|
|
|
AddMulti(m, start, end);
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RegisterRegion(Region reg)
|
|
|
|
|
{
|
|
|
|
|
var regName = reg.Name;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (regName == null)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (Regions.ContainsKey(regName))
|
|
|
|
|
{
|
2022-06-05 01:00:22 -07:00
|
|
|
logger.Warning("Duplicate region name '{RegionName}' for map '{MapName}'", regName, Name);
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Regions[regName] = reg;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public void UnregisterRegion(Region reg)
|
|
|
|
|
{
|
|
|
|
|
var regName = reg.Name;
|
|
|
|
|
|
|
|
|
|
if (regName != null)
|
|
|
|
|
{
|
|
|
|
|
Regions.Remove(regName);
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Point3D GetPoint(object o, bool eye)
|
|
|
|
|
{
|
|
|
|
|
Point3D p;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (o is Mobile mobile)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
p = mobile.Location;
|
|
|
|
|
p.Z += 14; // eye ? 15 : 10;
|
|
|
|
|
}
|
|
|
|
|
else if (o is Item item)
|
|
|
|
|
{
|
2023-02-05 12:09:08 -08:00
|
|
|
// Calculate the height based on the container, not the item inside.
|
|
|
|
|
var rootParent = item.RootParent;
|
|
|
|
|
if (rootParent != null)
|
2023-02-05 12:04:52 -08:00
|
|
|
{
|
2023-02-05 12:09:08 -08:00
|
|
|
p = GetPoint(rootParent, eye);
|
2023-02-05 12:04:52 -08:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
p = item.GetWorldLocation();
|
|
|
|
|
p.Z += item.ItemData.Height / 2 + 1;
|
|
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
else if (o is Point3D point3D)
|
|
|
|
|
{
|
|
|
|
|
p = point3D;
|
|
|
|
|
}
|
|
|
|
|
else if (o is LandTarget target)
|
|
|
|
|
{
|
|
|
|
|
p = target.Location;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
GetAverageZ(p.X, p.Y, out _, out _, out var top);
|
|
|
|
|
|
|
|
|
|
p.Z = top + 1;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
else if (o is StaticTarget st)
|
|
|
|
|
{
|
|
|
|
|
var id = TileData.ItemTable[st.ItemID & TileData.MaxItemValue];
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
p = new Point3D(st.X, st.Y, st.Z - id.CalcHeight + id.Height / 2 + 1);
|
|
|
|
|
}
|
|
|
|
|
else if (o is IPoint3D d)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
p = new Point3D(d.X, d.Y, d.Z);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2022-06-05 01:00:22 -07:00
|
|
|
logger.Warning("Warning: Invalid object ({Object}) in line of sight", o);
|
2022-03-22 20:07:32 -07:00
|
|
|
p = Point3D.Zero;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return p;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-26 09:39:46 -08:00
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
2022-03-22 20:07:32 -07:00
|
|
|
public bool CanFit(
|
2023-11-26 09:39:46 -08:00
|
|
|
Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true, bool requireSurface = true
|
2023-11-03 13:45:18 -07:00
|
|
|
) => CanFit(p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, checkMobiles, requireSurface);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-26 09:39:46 -08:00
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
2022-03-22 20:07:32 -07:00
|
|
|
public bool CanFit(
|
2023-11-26 09:39:46 -08:00
|
|
|
Point2D p, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true, bool requireSurface = true
|
2023-11-03 13:45:18 -07:00
|
|
|
) => CanFit(p.m_X, p.m_Y, z, height, checkBlocksFit, checkMobiles, requireSurface);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public bool CanFit(
|
|
|
|
|
int x, int y, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true,
|
|
|
|
|
bool requireSurface = true
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (this == Internal)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (x < 0 || y < 0 || x >= Width || y >= Height)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var hasSurface = false;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var lt = Tiles.GetLandTile(x, y);
|
|
|
|
|
GetAverageZ(x, y, out var lowZ, out var avgZ, out _);
|
|
|
|
|
var landFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if ((landFlags & TileFlag.Impassable) != 0 && avgZ > z && z + height > lowZ)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if ((landFlags & TileFlag.Impassable) == 0 && z == avgZ && !lt.Ignored)
|
|
|
|
|
{
|
|
|
|
|
hasSurface = true;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
bool surface, impassable;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-26 09:39:46 -08:00
|
|
|
foreach (var tile in Tiles.GetStaticAndMultiTiles(x, y))
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
2023-11-26 09:39:46 -08:00
|
|
|
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
2022-03-22 20:07:32 -07:00
|
|
|
surface = id.Surface;
|
|
|
|
|
impassable = id.Impassable;
|
|
|
|
|
|
2023-11-26 09:39:46 -08:00
|
|
|
if ((surface || impassable) && tile.Z + id.CalcHeight > z && z + height > tile.Z)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2020-08-25 18:53:35 -07:00
|
|
|
return false;
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-26 09:39:46 -08:00
|
|
|
if (surface && !impassable && z == tile.Z + id.CalcHeight)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2020-08-25 18:53:35 -07:00
|
|
|
hasSurface = true;
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var sector = GetSector(x, y);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
foreach (var item in sector.Items)
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
if (item is BaseMulti || item.ItemID > TileData.MaxItemValue || !item.AtWorldPoint(x, y))
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
continue;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
var id = item.ItemData;
|
|
|
|
|
surface = id.Surface;
|
|
|
|
|
impassable = id.Impassable;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
if ((surface || impassable || checkBlocksFit && item.BlocksFit) && item.Z + id.CalcHeight > z &&
|
|
|
|
|
z + height > item.Z)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (surface && !impassable && !item.Movable && z == item.Z + id.CalcHeight)
|
|
|
|
|
{
|
|
|
|
|
hasSurface = true;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (checkMobiles)
|
|
|
|
|
{
|
2023-10-29 22:42:46 -07:00
|
|
|
foreach (var m in sector.Mobiles)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
if (m.Location.m_X == x && m.Location.m_Y == y && (m.AccessLevel == AccessLevel.Player || !m.Hidden) &&
|
|
|
|
|
m.Z + 16 > z && z + height > m.Z)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return false;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return !requireSurface || hasSurface;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public bool CanSpawnMobile(Point3D p) => CanSpawnMobile(p.m_X, p.m_Y, p.m_Z);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public bool CanSpawnMobile(Point2D p, int z) => CanSpawnMobile(p.m_X, p.m_Y, z);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public bool CanSpawnMobile(int x, int y, int z) =>
|
|
|
|
|
Region.Find(new Point3D(x, y, z), this).AllowSpawn() && CanFit(x, y, z, 16);
|
2021-12-28 14:32:45 -08:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
private class ZComparer : IComparer<Item>
|
|
|
|
|
{
|
|
|
|
|
public static readonly ZComparer Default = new();
|
|
|
|
|
|
|
|
|
|
public int Compare(Item x, Item y) => x!.Z.CompareTo(y!.Z);
|
|
|
|
|
}
|
2021-12-28 14:32:45 -08:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Sector GetSector(Point3D p) => InternalGetSector(p.m_X >> SectorShift, p.m_Y >> SectorShift);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Sector GetSector(Point2D p) => InternalGetSector(p.m_X >> SectorShift, p.m_Y >> SectorShift);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Sector GetSector(int x, int y) => InternalGetSector(x >> SectorShift, y >> SectorShift);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Sector GetRealSector(int x, int y) => InternalGetSector(x, y);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
private Sector InternalGetSector(int x, int y)
|
|
|
|
|
{
|
|
|
|
|
if (x >= 0 && x < m_SectorsWidth && y >= 0 && y < m_SectorsHeight)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var xSectors = m_Sectors[x];
|
|
|
|
|
|
|
|
|
|
if (xSectors == null)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
m_Sectors[x] = xSectors = new Sector[m_SectorsHeight];
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var sec = xSectors[y];
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (sec == null)
|
|
|
|
|
{
|
|
|
|
|
xSectors[y] = sec = new Sector(x, y, this);
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return sec;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-05 08:17:34 -08:00
|
|
|
return _invalidSector;
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public bool LineOfSight(Point3D org, Point3D dest)
|
|
|
|
|
{
|
|
|
|
|
if (this == Internal)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (!Utility.InRange(org, dest, MaxLOSDistance))
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var end = dest;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (org.X > dest.X || org.X == dest.X && org.Y > dest.Y || org.X == dest.X && org.Y == dest.Y && org.Z > dest.Z)
|
|
|
|
|
{
|
|
|
|
|
(org, dest) = (dest, org);
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
int height;
|
|
|
|
|
Point3D p;
|
|
|
|
|
var path = new Point3DList();
|
|
|
|
|
TileFlag flags;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (org == dest)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (path.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
path.Clear();
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var xd = dest.m_X - org.m_X;
|
|
|
|
|
var yd = dest.m_Y - org.m_Y;
|
|
|
|
|
var zd = dest.m_Z - org.m_Z;
|
|
|
|
|
var zslp = Math.Sqrt(xd * xd + yd * yd);
|
|
|
|
|
var sq3d = zd != 0 ? Math.Sqrt(zslp * zslp + zd * zd) : zslp;
|
|
|
|
|
|
|
|
|
|
var rise = yd / sq3d;
|
|
|
|
|
var run = xd / sq3d;
|
|
|
|
|
zslp = zd / sq3d;
|
|
|
|
|
|
|
|
|
|
double y = org.m_Y;
|
|
|
|
|
double z = org.m_Z;
|
|
|
|
|
double x = org.m_X;
|
|
|
|
|
while (Utility.NumberBetween(x, dest.m_X, org.m_X, 0.5) && Utility.NumberBetween(y, dest.m_Y, org.m_Y, 0.5) &&
|
|
|
|
|
Utility.NumberBetween(z, dest.m_Z, org.m_Z, 0.5))
|
|
|
|
|
{
|
|
|
|
|
var ix = (int)Math.Round(x);
|
|
|
|
|
var iy = (int)Math.Round(y);
|
|
|
|
|
var iz = (int)Math.Round(z);
|
2020-08-25 18:53:35 -07:00
|
|
|
if (path.Count > 0)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
p = path.Last;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (p.m_X != ix || p.m_Y != iy || p.m_Z != iz)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
|
|
|
|
path.Add(ix, iy, iz);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
else
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
path.Add(ix, iy, iz);
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
x += run;
|
|
|
|
|
y += rise;
|
|
|
|
|
z += zslp;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (path.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
return true; // <--should never happen, but to be safe.
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
p = path.Last;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (p != dest)
|
|
|
|
|
{
|
|
|
|
|
path.Add(dest);
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
Point3D pTop = org, pBottom = dest;
|
|
|
|
|
Utility.FixPoints(ref pTop, ref pBottom);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var pathCount = path.Count;
|
|
|
|
|
var endTop = end.m_Z + 1;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
for (var i = 0; i < pathCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
var point = path[i];
|
|
|
|
|
var pointTop = point.m_Z + 1;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var landTile = Tiles.GetLandTile(point.X, point.Y);
|
|
|
|
|
GetAverageZ(point.m_X, point.m_Y, out var landZ, out _, out var landTop);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (landZ <= pointTop && landTop >= point.m_Z &&
|
|
|
|
|
(point.m_X != end.m_X || point.m_Y != end.m_Y || landZ > endTop || landTop < end.m_Z) &&
|
|
|
|
|
!landTile.Ignored)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
/* --Do land tiles need to be checked? There is never land between two people, always statics.--
|
|
|
|
|
LandTile landTile = Tiles.GetLandTile( point.X, point.Y );
|
|
|
|
|
if (landTile.Z-1 >= point.Z && landTile.Z+1 <= point.Z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Impassable) != 0)
|
|
|
|
|
return false;
|
|
|
|
|
*/
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var contains = false;
|
|
|
|
|
var ltID = landTile.ID;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
for (var j = 0; !contains && j < InvalidLandTiles.Length; ++j)
|
|
|
|
|
{
|
|
|
|
|
contains = ltID == InvalidLandTiles[j];
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-26 09:39:46 -08:00
|
|
|
bool foundStatic = false;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-11-26 09:39:46 -08:00
|
|
|
foreach (var t in Tiles.GetStaticAndMultiTiles(point.m_X, point.m_Y))
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
2023-11-26 09:39:46 -08:00
|
|
|
foundStatic = true;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
flags = id.Flags;
|
|
|
|
|
height = id.CalcHeight;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (t.Z <= pointTop && t.Z + height >= point.Z && (flags & (TileFlag.Window | TileFlag.NoShoot)) != 0)
|
|
|
|
|
{
|
|
|
|
|
if (point.m_X == end.m_X && point.m_Y == end.m_Y && t.Z <= endTop && t.Z + height >= end.m_Z)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
|
|
|
|
|
return false;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
2023-11-26 09:39:46 -08:00
|
|
|
|
|
|
|
|
if (contains && !foundStatic)
|
|
|
|
|
{
|
|
|
|
|
foreach (Item item in GetItemsAt(point))
|
|
|
|
|
{
|
|
|
|
|
if (item.Visible)
|
|
|
|
|
{
|
|
|
|
|
contains = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (contains)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var rect = new Rectangle2D(pTop.m_X, pTop.m_Y, pBottom.m_X - pTop.m_X + 1, pBottom.m_Y - pTop.m_Y + 1);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
foreach (var i in GetItemsInBounds(rect))
|
2022-03-22 20:07:32 -07:00
|
|
|
{
|
|
|
|
|
if (!i.Visible)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
continue;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (i is BaseMulti || i.ItemID > TileData.MaxItemValue)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var id = i.ItemData;
|
|
|
|
|
flags = id.Flags;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if ((flags & (TileFlag.Window | TileFlag.NoShoot)) == 0)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
height = id.CalcHeight;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var found = false;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var count = path.Count;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
for (var j = 0; j < count; ++j)
|
|
|
|
|
{
|
|
|
|
|
var point = path[j];
|
|
|
|
|
var pointTop = point.m_Z + 1;
|
|
|
|
|
var loc = i.Location;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
// if (t.Z <= point.Z && t.Z+height >= point.Z && ( height != 0 || ( t.Z == dest.Z && zd != 0 ) ))
|
|
|
|
|
if (loc.m_X == point.m_X && loc.m_Y == point.m_Y && loc.m_Z <= pointTop && loc.m_Z + height >= point.m_Z)
|
|
|
|
|
{
|
|
|
|
|
if (loc.m_X != end.m_X || loc.m_Y != end.m_Y || loc.m_Z > endTop || loc.m_Z + height < end.m_Z)
|
2020-09-12 15:31:21 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
found = true;
|
|
|
|
|
break;
|
2020-09-12 15:31:21 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (!found)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return false;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public bool LineOfSight(object from, object dest) =>
|
|
|
|
|
from == dest || (from as Mobile)?.AccessLevel > AccessLevel.Player ||
|
|
|
|
|
(dest as Item)?.RootParent == from || LineOfSight(GetPoint(from, true), GetPoint(dest, false));
|
|
|
|
|
|
|
|
|
|
public bool LineOfSight(Mobile from, Point3D target)
|
|
|
|
|
{
|
|
|
|
|
if (from.AccessLevel > AccessLevel.Player)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var eye = from.Location;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
eye.Z += 14;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return LineOfSight(eye, target);
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public bool LineOfSight(Mobile from, Mobile to)
|
|
|
|
|
{
|
|
|
|
|
if (from == to || from.AccessLevel > AccessLevel.Player)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var eye = from.Location;
|
|
|
|
|
var target = to.Location;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
eye.Z += 14;
|
|
|
|
|
target.Z += 14; // 10;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return LineOfSight(eye, target);
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
public Point3D GetRandomNearbyLocation(
|
|
|
|
|
Point3D loc, int maxRange = 2, int minRange = 0, int retryCount = 10,
|
|
|
|
|
int height = 16, bool checkBlocksFit = false,
|
|
|
|
|
bool checkMobiles = false
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
var j = 0;
|
|
|
|
|
var range = maxRange - minRange;
|
|
|
|
|
var locs = range <= 10 ? new bool[range + 1, range + 1] : null;
|
|
|
|
|
|
|
|
|
|
do
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var xRand = Utility.Random(range);
|
|
|
|
|
var yRand = Utility.Random(range);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (locs?[xRand, yRand] != true)
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
var x = loc.X + xRand + minRange;
|
|
|
|
|
var y = loc.Y + yRand + minRange;
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (CanFit(x, y, loc.Z, height, checkBlocksFit, checkMobiles))
|
2020-08-25 18:53:35 -07:00
|
|
|
{
|
2022-03-22 20:07:32 -07:00
|
|
|
loc = new Point3D(x, y, loc.Z);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
var z = GetAverageZ(x, y);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (CanFit(x, y, z, height, checkBlocksFit, checkMobiles))
|
|
|
|
|
{
|
|
|
|
|
loc = new Point3D(x, y, z);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
if (locs != null)
|
|
|
|
|
{
|
|
|
|
|
locs[xRand, yRand] = true;
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|
2022-03-22 20:07:32 -07:00
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
j++;
|
|
|
|
|
} while (j < retryCount);
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
return loc;
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
|
2022-03-22 20:07:32 -07:00
|
|
|
#pragma warning restore CA1000 // Do not declare static members on generic types
|
2022-11-12 00:26:02 -08:00
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public static Map Parse(string s) => Parse(s, null);
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public static Map Parse(string s, IFormatProvider provider) => Parse(s.AsSpan(), provider);
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
public static bool TryParse(string s, IFormatProvider provider, out Map result) =>
|
|
|
|
|
TryParse(s.AsSpan(), provider, out result);
|
|
|
|
|
|
|
|
|
|
public static Map Parse(ReadOnlySpan<char> s, IFormatProvider provider)
|
|
|
|
|
{
|
|
|
|
|
s = s.Trim();
|
|
|
|
|
|
|
|
|
|
if (s.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
throw new FormatException($"The input string '{s}' was not in a correct format.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s.InsensitiveEquals("Internal"))
|
|
|
|
|
{
|
|
|
|
|
return Internal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!int.TryParse(s, provider, out var index))
|
|
|
|
|
{
|
|
|
|
|
index = -1;
|
|
|
|
|
}
|
|
|
|
|
else if (index == 127)
|
|
|
|
|
{
|
|
|
|
|
return Internal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < Maps.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var map = Maps[i];
|
|
|
|
|
if (map == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= 0 && map.MapIndex == index || s.InsensitiveEquals(map.Name))
|
|
|
|
|
{
|
|
|
|
|
return map;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new FormatException($"The input string '{s}' was not in a correct format.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider provider, out Map result)
|
|
|
|
|
{
|
|
|
|
|
s = s.Trim();
|
|
|
|
|
|
|
|
|
|
if (s.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
result = default;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s.InsensitiveEquals("Internal"))
|
|
|
|
|
{
|
|
|
|
|
result = Internal;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!int.TryParse(s, provider, out var index))
|
|
|
|
|
{
|
|
|
|
|
index = -1;
|
|
|
|
|
}
|
|
|
|
|
else if (index == 127)
|
|
|
|
|
{
|
|
|
|
|
result = Internal;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < Maps.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var map = Maps[i];
|
|
|
|
|
if (map == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= 0 && map.MapIndex == index || s.InsensitiveEquals(map.Name))
|
|
|
|
|
{
|
|
|
|
|
result = map;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = default;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-10-15 11:20:49 -07:00
|
|
|
|
|
|
|
|
public class Sector
|
|
|
|
|
{
|
|
|
|
|
// TODO: Can we avoid this?
|
2023-10-16 20:51:02 -07:00
|
|
|
private static readonly List<Region> m_DefaultRectList = new();
|
2023-10-15 11:20:49 -07:00
|
|
|
private bool m_Active;
|
2023-10-30 18:49:00 -07:00
|
|
|
private ValueLinkList<NetState> _clients;
|
2023-10-16 20:51:02 -07:00
|
|
|
private ValueLinkList<Item> _items;
|
2023-10-29 22:42:46 -07:00
|
|
|
private ValueLinkList<Mobile> _mobiles;
|
2023-11-05 08:17:34 -08:00
|
|
|
private List<BaseMulti> _multis = new();
|
2023-10-16 20:51:02 -07:00
|
|
|
private List<Region> _regions;
|
2023-10-15 11:20:49 -07:00
|
|
|
|
|
|
|
|
public Sector(int x, int y, Map owner)
|
|
|
|
|
{
|
|
|
|
|
X = x;
|
|
|
|
|
Y = y;
|
|
|
|
|
Owner = owner;
|
|
|
|
|
m_Active = false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
public List<Region> Regions => _regions ?? m_DefaultRectList;
|
2023-10-15 11:20:49 -07:00
|
|
|
|
2023-11-05 08:17:34 -08:00
|
|
|
internal List<BaseMulti> Multis => _multis;
|
2023-10-15 11:20:49 -07:00
|
|
|
|
2023-10-29 22:42:46 -07:00
|
|
|
internal ref ValueLinkList<Mobile> Mobiles => ref _mobiles;
|
2023-10-15 11:20:49 -07:00
|
|
|
|
2023-10-26 17:49:15 -07:00
|
|
|
internal ref readonly ValueLinkList<Item> Items => ref _items;
|
2023-10-15 11:20:49 -07:00
|
|
|
|
2023-10-30 18:49:00 -07:00
|
|
|
internal ref readonly ValueLinkList<NetState> Clients => ref _clients;
|
2023-10-15 11:20:49 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
public bool Active => m_Active && Owner != Internal;
|
2023-10-15 11:20:49 -07:00
|
|
|
|
|
|
|
|
public Map Owner { get; }
|
|
|
|
|
|
|
|
|
|
public int X { get; }
|
|
|
|
|
|
|
|
|
|
public int Y { get; }
|
|
|
|
|
|
|
|
|
|
public void OnClientChange(NetState oldState, NetState newState)
|
|
|
|
|
{
|
2023-10-30 18:49:00 -07:00
|
|
|
var count = _clients.Count;
|
|
|
|
|
|
|
|
|
|
if (oldState != null)
|
|
|
|
|
{
|
|
|
|
|
_clients.Remove(oldState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newState != null)
|
|
|
|
|
{
|
|
|
|
|
_clients.AddLast(newState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_clients.Count == 0 && count > 0)
|
|
|
|
|
{
|
|
|
|
|
Owner.DeactivateSectors(X, Y);
|
|
|
|
|
}
|
|
|
|
|
else if (count == 0 && _clients.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
Owner.ActivateSectors(X, Y);
|
|
|
|
|
}
|
2023-10-15 11:20:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnEnter(Item item)
|
|
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
_items.AddLast(item);
|
2023-10-15 11:20:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnLeave(Item item)
|
|
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
_items.Remove(item);
|
2023-10-15 11:20:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnEnter(Mobile mob)
|
|
|
|
|
{
|
2023-10-29 22:42:46 -07:00
|
|
|
_mobiles.AddLast(mob);
|
2023-10-15 11:20:49 -07:00
|
|
|
|
|
|
|
|
if (mob.NetState != null)
|
|
|
|
|
{
|
2023-10-30 18:49:00 -07:00
|
|
|
_clients.AddLast(mob.NetState);
|
2023-10-15 11:20:49 -07:00
|
|
|
|
|
|
|
|
Owner.ActivateSectors(X, Y);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnLeave(Mobile mob)
|
|
|
|
|
{
|
2023-10-29 22:42:46 -07:00
|
|
|
_mobiles.Remove(mob);
|
2023-10-15 11:20:49 -07:00
|
|
|
|
|
|
|
|
if (mob.NetState != null)
|
|
|
|
|
{
|
2023-10-30 18:49:00 -07:00
|
|
|
_clients.Remove(mob.NetState);
|
2023-10-15 11:20:49 -07:00
|
|
|
|
|
|
|
|
Owner.DeactivateSectors(X, Y);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnEnter(Region region, Rectangle3D rect)
|
|
|
|
|
{
|
2024-02-18 12:01:16 -08:00
|
|
|
if (_regions?.Contains(region) == true)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
Utility.Add(ref _regions, region);
|
2023-10-15 11:20:49 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
_regions.Sort();
|
2023-10-15 11:20:49 -07:00
|
|
|
|
|
|
|
|
UpdateMobileRegions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnLeave(Region region)
|
|
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
if (_regions != null)
|
2023-10-15 11:20:49 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
for (var i = _regions.Count - 1; i >= 0; i--)
|
2023-10-15 11:20:49 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
var r = _regions[i];
|
2023-10-15 11:20:49 -07:00
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
if (r == region)
|
2023-10-15 11:20:49 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
_regions.RemoveAt(i);
|
2023-10-15 11:20:49 -07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-16 20:51:02 -07:00
|
|
|
if (_regions.Count == 0)
|
2023-10-15 11:20:49 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
_regions = null;
|
2023-10-15 11:20:49 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateMobileRegions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateMobileRegions()
|
|
|
|
|
{
|
2023-10-29 22:42:46 -07:00
|
|
|
if (_mobiles.Count > 0)
|
2023-10-15 11:20:49 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
using var queue = PooledRefQueue<Mobile>.Create(_mobiles.Count);
|
|
|
|
|
foreach (var mob in _mobiles)
|
2023-10-15 11:20:49 -07:00
|
|
|
{
|
|
|
|
|
queue.Enqueue(mob);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (queue.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
queue.Dequeue().UpdateRegion();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnMultiEnter(BaseMulti multi)
|
|
|
|
|
{
|
2023-11-05 08:17:34 -08:00
|
|
|
_multis.Add(multi);
|
2023-10-15 11:20:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnMultiLeave(BaseMulti multi)
|
|
|
|
|
{
|
2023-11-05 08:17:34 -08:00
|
|
|
_multis.Remove(multi);
|
2023-10-15 11:20:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Activate()
|
|
|
|
|
{
|
|
|
|
|
if (!Active)
|
|
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
foreach (var item in _items)
|
2023-10-15 11:20:49 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
item.OnSectorActivate();
|
2023-10-15 11:20:49 -07:00
|
|
|
}
|
|
|
|
|
|
2023-10-29 22:42:46 -07:00
|
|
|
foreach (var mob in _mobiles)
|
2023-10-15 11:20:49 -07:00
|
|
|
{
|
2023-10-29 22:42:46 -07:00
|
|
|
mob.OnSectorActivate();
|
2023-10-15 11:20:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_Active = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Deactivate()
|
|
|
|
|
{
|
|
|
|
|
if (Active)
|
|
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
foreach (var item in _items)
|
2023-10-15 11:20:49 -07:00
|
|
|
{
|
2023-10-16 20:51:02 -07:00
|
|
|
item.OnSectorDeactivate();
|
2023-10-15 11:20:49 -07:00
|
|
|
}
|
|
|
|
|
|
2023-10-29 22:42:46 -07:00
|
|
|
foreach (var mob in _mobiles)
|
2023-10-15 11:20:49 -07:00
|
|
|
{
|
2023-10-29 22:42:46 -07:00
|
|
|
mob.OnSectorDeactivate();
|
2023-10-15 11:20:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_Active = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-25 18:53:35 -07:00
|
|
|
}
|