PSO2SERVER/Server/Zone/Map.cs
2024-09-11 17:13:00 +08:00

219 lines
7.0 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using PSO2SERVER.Models;
using PSO2SERVER.Object;
using PSO2SERVER.Packets;
using PSO2SERVER.Packets.PSOPackets;
namespace PSO2SERVER.Zone
{
public class Map
{
public string Name { get; set; }
public MapType Type { get; set; }
public PSOObject[] Objects { get; set; }
public PSONPC[] NPCs { get; set; }
public ObjectHeader MapHeader { get; set; }
public int MapID { get; set; }
public int VariantID { get; set; }
public MapFlags Flags { get; set; }
public List<Client> Clients { get; set; }
public GenParam GenerationArgs { get; set; }
public string InstanceName { get; set; }
public enum MapType : int
{
Lobby = 4,
Casino = 11,
MyRoom = 8,
TeamRoom = 7,
ChallengeLobby = 5,
Campship = 1,
Quest = 0,
Other = ~0
};
[Flags]
public enum MapFlags : uint
{
None = 0,
MultiPartyArea = 1,
Unknown1 = 2,
EnableMap = 4
}
public Map(string name, int id, int variant, MapType type, MapFlags flags)
{
Name = name;
MapID = id;
VariantID = variant;
Type = type;
Flags = flags;
Clients = new List<Client>();
GenerationArgs = new GenParam();
Objects = ObjectManager.Instance.GetObjectsForZone(Name);
NPCs = ObjectManager.Instance.getNPCSForZone(Name);
}
public PSOLocation GetDefaultLocation()
{
PSOLocation location;
switch (Type)
{
case MapType.Lobby:
location = new PSOLocation(0f, 1f, 0f, 0f, -0.417969f, 0f, 137.375f);
break;
case MapType.Casino:
location = new PSOLocation(0, 1f, 0, 0, 2, 6, 102);
break;
default:
location = new PSOLocation(0f, 1f, 0f, 0f, 0f, 0f, 0f);
break;
}
return location;
}
/// <summary>
/// Spawns a client into a map at a given location
/// </summary>
/// <param name="c">Client to spawn into map</param>
/// <param name="location">Location to spawn client at</param>
/// <param name="questOveride">If this also sets the quest data, specify the quest name here to load the spawn from the bin rather then generate it.</param>
public void SpawnClient(Client c, PSOLocation location, string questOveride = "")
{
if (Clients.Contains(c))
{
return;
}
// Set area
if (questOveride != "") // TODO: This is a temporary hack, fix me!!
{
var setAreaPacket = File.ReadAllBytes("Resources/quests/" + questOveride + ".bin");
c.SendPacket(0x03, 0x24, 4, setAreaPacket);
}
else
{
PacketWriter writer = new PacketWriter();
writer.WriteStruct(new ObjectHeader(3, EntityType.Map));
writer.WriteStruct(new ObjectHeader((uint)c.User.PlayerId, EntityType.Player));
writer.Write(0x1); // 8 Zeros
writer.Write(0); // 8 Zeros
writer.Write(~(uint)Type); // F4 FF FF FF
writer.Write(MapID); // Map ID maybe
writer.Write((uint)Flags);
writer.Write(GenerationArgs.seed); // 81 8F E6 19 (Maybe seed)
writer.Write(VariantID); // Randomgen enable / disable maybe
writer.Write(GenerationArgs.xsize); // X Size
writer.Write(GenerationArgs.ysize); // Y Size
writer.Write(1);
writer.Write(1);
writer.Write(~0); // FF FF FF FF FF FF FF FF
writer.Write(0x301);
c.SendPacket(0x3, 0x0, 0x0, writer.ToArray());
}
if (c.CurrentZone != null)
{
c.CurrentZone.RemoveClient(c);
}
var setPlayerId = new PacketWriter();
setPlayerId.WritePlayerHeader((uint)c.User.PlayerId);
c.SendPacket(0x06, 0x00, 0, setPlayerId.ToArray());
// Spawn Character
c.SendPacket(new CharacterSpawnPacket(c.Character, location));
c.CurrentLocation = location;
c.CurrentZone = this;
// Objects
foreach (PSOObject obj in Objects)
{
c.SendPacket(0x08, 0x0B, 0x0, obj.GenerateSpawnBlob());
}
// NPCs
foreach (PSONPC npc in NPCs)
{
c.SendPacket(0x08, 0xC, 0x4, npc.GenerateSpawnBlob());
}
// Spawn for others, Spawn others for me
CharacterSpawnPacket spawnMe = new CharacterSpawnPacket(c.Character, location, false);
foreach (Client other in Clients)
{
other.SendPacket(spawnMe);
c.SendPacket(new CharacterSpawnPacket(other.Character, other.CurrentLocation, false));
}
// Unlock Controls
c.SendPacket(new NoPayloadPacket(0x03, 0x2B)); // Inital spawn only, move this!
Clients.Add(c);
Logger.Write("[MAP] {0} has spawned in {1}.", c.User.Username, Name);
if (InstanceName != null && ZoneManager.Instance.playerCounter.ContainsKey(InstanceName))
{
ZoneManager.Instance.playerCounter[InstanceName] += 1;
}
}
public void RemoveClient(Client c)
{
if (!Clients.Contains(c))
return;
c.CurrentZone = null;
Clients.Remove(c);
foreach (Client other in Clients)
{
PacketWriter writer = new PacketWriter();
writer.WriteStruct(new ObjectHeader((uint)other.User.PlayerId, EntityType.Player));
writer.WriteStruct(new ObjectHeader((uint)c.User.PlayerId, EntityType.Player));
other.SendPacket(0x4, 0x3B, 0x40, writer.ToArray());
}
if (InstanceName != null && ZoneManager.Instance.playerCounter.ContainsKey(InstanceName))
{
ZoneManager.Instance.playerCounter[InstanceName] -= 1;
if (ZoneManager.Instance.playerCounter[InstanceName] <= 0)
{
ZoneManager.Instance.playerCounter.Remove(InstanceName);
ZoneManager.Instance.instances.Remove(InstanceName);
}
}
}
public class GenParam
{
public GenParam()
{
}
public GenParam(uint seed, uint x, uint y)
{
this.seed = seed;
this.xsize = x;
this.ysize = y;
}
public uint seed, xsize, ysize;
}
}
}