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(); Objects = new List(); Players = new List(); Bosses = new List(); NPCs = new List(); } 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 ValidCells { get; private set;} public List Objects { get; private set;} public List Players { get; private set;} public List Bosses { get; private set;} public List NPCs { get; private set;} public HashSet[] OrderedObjects; private readonly List Guards = new List(); 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[Width]; for (int i = 0; i < OrderedObjects.Length; i++) OrderedObjects[i] = new HashSet(); } 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 GetCells(Point location, int minRadius, int maxRadius) { List cells = new List(); 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 Objects; public SafeZoneInfo SafeZone; public List Movements; public bool HasMovement; public Cell(Point location) { Location = location; } public void AddObject(MapObject ob) { if (Objects == null) Objects = new List(); 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; } } }