Files
2025-04-25 16:53:07 +08:00

522 lines
17 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using Library;
using Library.Network;
using Library.SystemModels;
using Server.DBModels;
using Server.Envir;
using Zircon.Server.Models.Monsters;
using S = Library.Network.ServerPackets;
namespace Zircon.Server.Models
{
public sealed class Map
{
private void _init()
{
ValidCells = new List<Cell>();
Objects = new List<MapObject>();
Players = new List<PlayerObject>();
Bosses = new List<MonsterObject>();
NPCs = new List<NPCObject>();
}
public Map()
{
_init();
}
public MapInfo Info { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public bool HasSafeZone { get; set; }
public Cell[,] Cells { get; private set; }
public List<Cell> ValidCells { get; private set;}
public List<MapObject> Objects { get; private set;}
public List<PlayerObject> Players { get; private set;}
public List<MonsterObject> Bosses { get; private set;}
public List<NPCObject> NPCs { get; private set;}
public HashSet<MapObject>[] OrderedObjects;
private readonly List<MonsterObject> Guards = new List<MonsterObject>();
public DateTime LastProcess;
public DateTime HalloweenEventTime, ChristmasEventTime;
public Map(MapInfo info)
{
_init();
Info = info;
}
public void Load()
{
string fileName = string.Format("{0}{1}.map", Config.MapPath, Info.FileName);
if (!File.Exists(fileName))
{
SEnvir.Log(string.Format("没有找到地图: {0}.", fileName));
return;
}
byte[] fileBytes = File.ReadAllBytes(fileName);
Width = fileBytes[23] << 8 | fileBytes[22];
Height = fileBytes[25] << 8 | fileBytes[24];
Cells = new Cell[Width, Height];
int offSet = 28 + Width * Height / 4 * 3;
for (int x = 0; x < Width; x++)
for (int y = 0; y < Height; y++)
{
byte flag = fileBytes[offSet + (x * Height + y) * 14];
if ((flag & 0x02) != 2 || (flag & 0x01) != 1) continue;
if (ValidCells != null)
ValidCells.Add(Cells[x, y] = new Cell(new Point(x, y)) { Map = this });
}
OrderedObjects = new HashSet<MapObject>[Width];
for (int i = 0; i < OrderedObjects.Length; i++)
OrderedObjects[i] = new HashSet<MapObject>();
}
public void Setup()
{
CreateGuards();
}
public void CreateGuards()
{
foreach (GuardInfo info in Info.Guards)
{
MonsterObject mob = MonsterObject.GetMonster(info.Monster);
mob.Direction = info.Direction;
if (!mob.Spawn(Info, new Point(info.X, info.Y)))
{
SEnvir.Log(string.Format("创建地图护卫失败:{0}, 位置: {1}, {2}", Info.Description, info.X, info.Y));
continue;
}
Guards.Add(mob);
}
}
public void ClearGuards()
{
foreach(var guard in Guards)
guard.Despawn();
Guards.Clear();
}
public void Process()
{
}
public void AddObject(MapObject ob)
{
Objects.Add(ob);
switch (ob.Race)
{
case ObjectType.Player:
Players.Add((PlayerObject) ob);
break;
case ObjectType.Item:
break;
case ObjectType.NPC:
NPCs.Add((NPCObject) ob);
break;
case ObjectType.Spell:
break;
case ObjectType.Monster:
MonsterObject mob = (MonsterObject)ob;
if (mob.MonsterInfo.IsBoss)
Bosses.Add(mob);
break;
}
}
public void RemoveObject(MapObject ob)
{
Objects.Remove(ob);
switch (ob.Race)
{
case ObjectType.Player:
Players.Remove((PlayerObject)ob);
break;
case ObjectType.Item:
break;
case ObjectType.NPC:
NPCs.Remove((NPCObject)ob);
break;
case ObjectType.Spell:
break;
case ObjectType.Monster:
MonsterObject mob = (MonsterObject)ob;
if (mob.MonsterInfo.IsBoss)
Bosses.Remove(mob);
break;
}
}
public Cell GetCell(int x, int y)
{
if (x < 0 || x >= Width || y < 0 || y >= Height) return null;
return Cells[x, y];
}
public Cell GetCell(Point location)
{
return GetCell(location.X, location.Y);
}
public List<Cell> GetCells(Point location, int minRadius, int maxRadius)
{
List<Cell> cells = new List<Cell>();
for (int d = 0; d <= maxRadius; d++)
{
for (int y = location.Y - d; y <= location.Y + d; y++)
{
if (y < 0) continue;
if (y >= Height) break;
for (int x = location.X - d; x <= location.X + d; x += Math.Abs(y - location.Y) == d ? 1 : d*2)
{
if (x < 0) continue;
if (x >= Width) break;
Cell cell = Cells[x, y]; //Direct Access we've checked the boudaries.
if (cell == null) continue;
cells.Add(cell);
}
}
}
return cells;
}
public Point GetRandomLocation()
{
return ValidCells.Count > 0 ? ValidCells[SEnvir.Random.Next(ValidCells.Count)].Location : Point.Empty;
}
public Point GetRandomLocation(Point location, int range, int attempts = 25)
{
int minX = Math.Max(0, location.X - range);
int maxX = Math.Min(Width, location.X + range);
int minY = Math.Max(0, location.Y - range);
int maxY = Math.Min(Height, location.Y + range);
for (int i = 0; i < attempts; i++)
{
Point test = new Point(SEnvir.Random.Next(minX, maxX), SEnvir.Random.Next(minY, maxY));
if (GetCell(test) != null)
return test;
}
return Point.Empty;
}
public Point GetRandomLocation(int minX, int maxX, int minY, int maxY, int attempts = 25)
{
for (int i = 0; i < attempts; i++)
{
Point test = new Point(SEnvir.Random.Next(minX, maxX), SEnvir.Random.Next(minY, maxY));
if (GetCell(test) !=null)
return test;
}
return Point.Empty;
}
public void Broadcast(Point location, Packet p)
{
foreach (PlayerObject player in Players)
{
if (!Functions.InRange(location, player.CurrentLocation, Config.MaxViewRange)) continue;
player.Enqueue(p);
}
}
public void Broadcast(Packet p)
{
foreach (PlayerObject player in Players)
player.Enqueue(p);
}
}
public class SpawnInfo
{
public RespawnInfo Info;
public Map CurrentMap;
public DateTime NextSpawn;
public int AliveCount;
public DateTime LastCheck;
public SpawnInfo(RespawnInfo info)
{
Info = info;
CurrentMap = SEnvir.GetMap(info.Region.Map);
LastCheck = SEnvir.Now;
}
public void DoSpawn(bool eventSpawn)
{
if (!eventSpawn)
{
if (Info.EventSpawn || SEnvir.Now < NextSpawn) return;
if (Info.Delay >= 1000000)
{
TimeSpan timeofDay = TimeSpan.FromMinutes(Info.Delay - 1000000);
if (LastCheck.TimeOfDay >= timeofDay || SEnvir.Now.TimeOfDay < timeofDay)
{
LastCheck = SEnvir.Now;
return;
}
LastCheck = SEnvir.Now;
}
else
{
if (Info.Announce)
NextSpawn = SEnvir.Now.AddSeconds(Info.Delay * 60);
else
NextSpawn = SEnvir.Now.AddSeconds(SEnvir.Random.Next(Info.Delay * 60) + Info.Delay * 30);
}
}
for (int i = AliveCount; i < Info.Count; i++)
{
MonsterObject mob = MonsterObject.GetMonster(Info.Monster);
if (!Info.Monster.IsBoss)
{
if (SEnvir.Now > CurrentMap.HalloweenEventTime && SEnvir.Now <= Config.HalloweenEventEnd)
{
mob = new HalloweenMonster {MonsterInfo = Info.Monster, HalloweenEventMob = true};
CurrentMap.HalloweenEventTime = SEnvir.Now.AddHours(1);
}
else if (SEnvir.Now > CurrentMap.ChristmasEventTime && SEnvir.Now <= Config.ChristmasEventEnd)
{
mob = new ChristmasMonster { MonsterInfo = Info.Monster, ChristmasEventMob = true};
CurrentMap.ChristmasEventTime = SEnvir.Now.AddMinutes(20);
}
}
mob.SpawnInfo = this;
if (!mob.Spawn(Info.Region))
{
mob.SpawnInfo = null;
continue;
}
if (Info.Announce)
{
if (Info.Delay >= 1000000)
{
foreach (SConnection con in SEnvir.Connections)
con.ReceiveChat(string.Format("{0} 出现了.", mob.MonsterInfo.MonsterName), MessageType.System);
}
else
{
foreach (SConnection con in SEnvir.Connections)
con.ReceiveChat(string.Format(con.Language.BossSpawn, CurrentMap.Info.Description), MessageType.System);
}
}
mob.DropSet = Info.DropSet;
AliveCount++;
}
}
}
public class Cell
{
public Point Location;
public Map Map;
public List<MapObject> Objects;
public SafeZoneInfo SafeZone;
public List<MovementInfo> Movements;
public bool HasMovement;
public Cell(Point location)
{
Location = location;
}
public void AddObject(MapObject ob)
{
if (Objects == null)
Objects = new List<MapObject>();
Objects.Add(ob);
ob.CurrentMap = Map;
ob.CurrentLocation = Location;
Map.OrderedObjects[Location.X].Add(ob);
}
public void RemoveObject(MapObject ob)
{
Objects.Remove(ob);
if (Objects.Count == 0)
Objects = null;
Map.OrderedObjects[Location.X].Remove(ob);
}
public bool IsBlocking(MapObject checker,bool cellTime)
{
if (Objects == null) return false;
foreach (MapObject ob in Objects)
{
if (!ob.Blocking) continue;
if (cellTime && SEnvir.Now < ob.CellTime) continue;
if (ob.Stats == null) return true;
if (ob.Buffs.Any(x => x.Type == BuffType.Cloak || x.Type == BuffType.Transparency) && ob.Level > checker.Level && !ob.InGroup(checker)) continue;
return true;
}
return false;
}
public Cell GetMovement(MapObject ob)
{
if (Movements == null || Movements.Count == 0)
return this;
for (int i = 0; i < 5; i++) //20 Attempts to get movement;
{
MovementInfo movement = Movements[SEnvir.Random.Next(Movements.Count)];
Map map = SEnvir.GetMap(movement.DestinationRegion.Map);
Cell cell = map.GetCell(movement.DestinationRegion.PointList[SEnvir.Random.Next(movement.DestinationRegion.PointList.Count)]);
if (cell == null) continue;
if (ob.Race == ObjectType.Player)
{
PlayerObject player = (PlayerObject)ob;
if (movement.DestinationRegion.Map.MinimumLevel > ob.Level && !player.GameMaster)
{
player.Connection.ReceiveChat(string.Format(player.Connection.Language.NeedLevel, movement.DestinationRegion.Map.MinimumLevel), MessageType.System);
foreach (SConnection con in player.Connection.Observers)
con.ReceiveChat(string.Format(con.Language.NeedLevel, movement.DestinationRegion.Map.MinimumLevel), MessageType.System);
break;
}
if (movement.DestinationRegion.Map.MaximumLevel > 0 && movement.DestinationRegion.Map.MaximumLevel < ob.Level && !player.GameMaster)
{
player.Connection.ReceiveChat(string.Format(player.Connection.Language.NeedMaxLevel, movement.DestinationRegion.Map.MaximumLevel), MessageType.System);
foreach (SConnection con in player.Connection.Observers)
con.ReceiveChat(string.Format(con.Language.NeedMaxLevel, movement.DestinationRegion.Map.MaximumLevel), MessageType.System);
break;
}
if (movement.NeedSpawn != null)
{
SpawnInfo spawn = SEnvir.Spawns.FirstOrDefault(x => x.Info == movement.NeedSpawn);
if (spawn == null)
break;
if (spawn.AliveCount == 0)
{
player.Connection.ReceiveChat(player.Connection.Language.NeedMonster, MessageType.System);
foreach (SConnection con in player.Connection.Observers)
con.ReceiveChat(con.Language.NeedMonster, MessageType.System);
break;
}
}
if (movement.NeedItem != null)
{
if (player.GetItemCount(movement.NeedItem) == 0)
{
player.Connection.ReceiveChat(string.Format(player.Connection.Language.NeedItem, movement.NeedItem.ItemName), MessageType.System);
foreach (SConnection con in player.Connection.Observers)
con.ReceiveChat(string.Format(con.Language.NeedItem, movement.NeedItem.ItemName), MessageType.System);
break;
}
player.TakeItem(movement.NeedItem, 1);
}
switch (movement.Effect)
{
case MovementEffect.SpecialRepair:
player.SpecialRepair(EquipmentSlot.Weapon);
player.SpecialRepair(EquipmentSlot.Shield);
player.SpecialRepair(EquipmentSlot.Helmet);
player.SpecialRepair(EquipmentSlot.Armour);
player.SpecialRepair(EquipmentSlot.Necklace);
player.SpecialRepair(EquipmentSlot.BraceletL);
player.SpecialRepair(EquipmentSlot.BraceletR);
player.SpecialRepair(EquipmentSlot.RingL);
player.SpecialRepair(EquipmentSlot.RingR);
player.SpecialRepair(EquipmentSlot.Shoes);
player.RefreshStats();
break;
}
}
return cell.GetMovement(ob);
}
return this;
}
}
}