Na začátku hry se mapa na serveru synchronizuje s mapou u clienta. Rozšířen spritesheet monitoru o remote dveře. Přidána GlobalClassLib pro kód sdílený mezi clientem a serverem. Základ pro implementaci ovládání remote dveří.

This commit is contained in:
Perry 2026-02-14 14:35:29 +01:00
parent 8801a7c919
commit 7e6b3d724b
25 changed files with 374 additions and 67 deletions

View file

@ -6,6 +6,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNAF_Server", "FNAF_Server\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PacketLib", "PacketLib\PacketLib.csproj", "{EDC8B98F-D2F7-4BDB-8E3B-E3120A743023}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GlobalClassLib", "GlobalClassLib\GlobalClassLib.csproj", "{4AC67290-B9A4-4CF3-A0A8-62DAAD0CF068}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -24,5 +26,9 @@ Global
{EDC8B98F-D2F7-4BDB-8E3B-E3120A743023}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EDC8B98F-D2F7-4BDB-8E3B-E3120A743023}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EDC8B98F-D2F7-4BDB-8E3B-E3120A743023}.Release|Any CPU.Build.0 = Release|Any CPU
{4AC67290-B9A4-4CF3-A0A8-62DAAD0CF068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4AC67290-B9A4-4CF3-A0A8-62DAAD0CF068}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AC67290-B9A4-4CF3-A0A8-62DAAD0CF068}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AC67290-B9A4-4CF3-A0A8-62DAAD0CF068}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View file

@ -2,6 +2,8 @@ using System;
using System.Net;
using System.Net.Sockets;
using FNAF_Clone.GUI;
using FNAF_Clone.Map;
using GlobalClassLib;
using LiteNetLib;
using LiteNetLib.Utils;
using PacketLib;
@ -26,7 +28,7 @@ public class Client {
processor.SubscribeReusable<JoinAcceptPacket>(OnJoinAccept);
processor.SubscribeReusable<UpdatePlayerPacket>(OnPlayerUpdate);
processor.SubscribeReusable<MapInitPacket>(OnMapInit);
client = new NetManager(listener){
AutoRecycle = true
@ -59,20 +61,29 @@ public class Client {
SendPacket(new PlayerCommandPacket{commands = pCommands}, DeliveryMethod.ReliableOrdered);
}
public static void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) {
private static void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) {
processor.ReadAllPackets(reader, peer);
}
public static void OnJoinAccept(JoinAcceptPacket packet) {
private static void OnJoinAccept(JoinAcceptPacket packet) {
Console.WriteLine($"Accepted by server, pid: {packet.state.pid}");
Player.state = packet.state;
}
private static void OnMapInit(MapInitPacket packet) {
(int id1, int id2, ConnectorType type)[] connectors = new (int id1, int id2, ConnectorType type)[packet.Connectors.Length / 3];
for (int i = 0; i < packet.Connectors.Length / 3; i++){
connectors[i] = (packet.Connectors[i * 3], packet.Connectors[i * 3 + 1], (ConnectorType)packet.Connectors[i * 3 + 2]);
}
ClientMapManager.InitMap(connectors);
}
public static void Update() {
client.PollEvents();
}
public static void OnPlayerUpdate(UpdatePlayerPacket packet) { // TODO: move this to a separate class
//Player.state = Player.state.pid == 0 ? packet.stateP1 : packet.stateP2;
foreach (var e in packet.events){
@ -96,7 +107,7 @@ public class Client {
break;
case 4: // toggle door
Player.state.doorStates[e.Args[1]] = e.Args[2] == 1;
UIManager.ChangeDoorState(e.Args[1], e.Args[2] == 1);
UIManager.ChangeDoorState((Direction)e.Args[1], e.Args[2] == 1);
Console.WriteLine($"E: Player {e.Args[0]} {(e.Args[2] == 1 ? "closed" : "opened")} door {e.Args[1]}");
break;
case -1: // movement

View file

@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FNAF_Clone.GUI;
using FNAF_Clone.Map;
using GlobalClassLib;
using Microsoft.Xna.Framework.Input;
using MonoGameLibrary.Input;
using PacketLib;
@ -9,11 +12,11 @@ namespace FNAF_Clone;
public class CommandManager {
private static (string label, Keys key, Action action)[] keybinds = [
("Toggle Camera", Keys.Space, SendToggleCamera),
("Toggle camera", Keys.Space, SendToggleCamera),
("Toggle left door", Keys.A, ToggleDoorLeft),
("Toggle centre door", Keys.W, ToggleDoorCentre),
("Toggle right door", Keys.D, ToggleDoorRight)
("Toggle right door", Keys.D, ToggleDoorRight),
("Toggle back door", Keys.S, ToggleDoorBack)
];
private static InputListenerHook toggleCamHook = new(true);
@ -26,16 +29,25 @@ public class CommandManager {
Client.SendCommands([PlayerCommand.TOGGLE_MONITOR()]);
}
private static void ToggleDoorLeft() => SendToggleDoor(0);
private static void ToggleDoorCentre() => SendToggleDoor(1);
private static void ToggleDoorRight() => SendToggleDoor(2);
private static void ToggleDoorLeft() => SendToggleDoor(Direction.EAST);
private static void ToggleDoorCentre() => SendToggleDoor(Direction.NORTH);
private static void ToggleDoorRight() => SendToggleDoor(Direction.WEST);
private static void ToggleDoorBack() => SendToggleDoor(Direction.SOUTH);
private static void SendToggleDoor(int id) {
private static void SendToggleDoor(Direction direction) {
if (Screen.CurrentScreen.Label == UIManager.ScreenTypes.CAMERAS){
//TODO: camera doors
SendToggleRemoteDoor(direction);
return;
}
Client.SendCommands([PlayerCommand.TOGGLE_DOOR_OFFICE(id)]);
Client.SendCommands([PlayerCommand.TOGGLE_DOOR_OFFICE(direction)]);
}
private static void SendToggleRemoteDoor(Direction direction) { // WIP
TileConnectorProjection[] connectors = ClientMapManager.GetConnectors(Client.Player.state.camera).Where(c => c.Type == ConnectorType.DOOR_REMOTE).ToArray();
// Client.SendCommands([PlayerCommand.TOGGLE_DOOR_REMOTE()]);
}
public static void SendChangeCamera(int idx, int idy) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Before After
Before After

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<TextureAtlas count="5">
<TextureAtlas count="19">
<Texture>images/SpriteSheet_monitor</Texture>
<Regions>
<Region id = "0" name="screen" x="0" y="0" width="640" height="360"/>
@ -8,5 +8,24 @@
<Region id = "3" name="map" x="408" y="360" width="164" height="171"/>
<Region id = "4" name="light-button" x="244" y="531" width="36" height="42"/>
<Region id = "5" name="door-remote-open" x="280" y="531" width="32" height="33"/>
<Region id = "6" name="door-remote-closed" x="312" y="531" width="32" height="33"/>
<Region id = "7" name="door-office-p1-left-open" x="344" y="531" width="32" height="33"/>
<Region id = "8" name="door-office-p1-left-closed" x="376" y="531" width="32" height="33"/>
<Region id = "9" name="door-office-p1-centre-open" x="408" y="531" width="32" height="33"/>
<Region id = "10" name="door-office-p1-centre-closed" x="440" y="531" width="32" height="33"/>
<Region id = "11" name="door-office-p1-right-open" x="472" y="531" width="32" height="33"/>
<Region id = "12" name="door-office-p1-right-closed" x="504" y="531" width="32" height="33"/>
<Region id = "13" name="door-office-p2-right-open" x="344" y="564" width="32" height="33"/>
<Region id = "14" name="door-office-p2-right-closed" x="376" y="564" width="32" height="33"/>
<Region id = "15" name="door-office-p2-centre-open" x="408" y="564" width="32" height="33"/>
<Region id = "16" name="door-office-p2-centre-closed" x="440" y="564" width="32" height="33"/>
<Region id = "17" name="door-office-p2-left-open" x="472" y="564" width="32" height="33"/>
<Region id = "18" name="door-office-p2-left-closed" x="504" y="564" width="32" height="33"/>
</Regions>
</TextureAtlas>

View file

@ -39,6 +39,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GlobalClassLib\GlobalClassLib.csproj" />
<ProjectReference Include="..\PacketLib\PacketLib.csproj" />
</ItemGroup>
<ItemGroup>

View file

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using GlobalClassLib;
using Microsoft.Xna.Framework;
using MonoGameLibrary;
using MonoGameLibrary.Graphics;
@ -45,20 +47,31 @@ public class UIManager {
monitorScreen.AddElement("map-frame", new UIElement(monitorAtlas[2], new Point(334, 135)));
monitorScreen.AddElement("map", new UIElement(monitorAtlas[3], new Point(334, 135)));
for (int i = 0; i < 5; i++){
Dictionary<(int,int),string> doorPositions = new(){
{(0, 0),"2-5"},{ (1, 0), "2-4" }, { (1, 1), "2-3" }, { (3, 0), "2-2" }, { (3, 1), "2-1" }, { (4, 0), "2-0" }, // TODO: generate this dynamically from server map info
{(0, 3),"1-0"},{ (1, 3), "1-1" }, { (1, 2), "1-2" }, { (3, 3), "1-3" }, { (3, 2), "1-4" }, { (4, 3), "1-5" }
};
for (int i = 0; i < 5; i++){ // NOTE: this loop does y in reverse, y labels are inverted to match server
for (int j = 0; j < 5; j++){
int i1 = i;
int j1 = j;
monitorScreen.AddElement($"room{i}-{j}", new UIElement(new Point(336 + (32 * i), 144 + (32 * j)), new Point(367 + (32 * i), 175 + (32 * j)))
{Pressable = true, OnMousePress = (() => CommandManager.SendChangeCamera(i1, j1))});
Point point1 = new Point(336 + (32 * i), 144 + (32 * j));
Point point2 = new Point(367 + (32 * i), 175 + (32 * j));
monitorScreen.AddElement($"room{i}-{4 - j}", new UIElement(point1, point2)
{Pressable = true, OnMousePress = (() => CommandManager.SendChangeCamera(i1, 4 - j1))});
if (doorPositions.ContainsKey((i, j))){
monitorScreen.AddElement("door"+doorPositions[(i, j)], new UIElement([monitorAtlas[5], monitorAtlas[6]], point1));
}
}
}
}
public static void ChangeDoorState(int id, bool state) {
switch (id){
public static void ChangeDoorState(Direction dir, bool state) {
switch ((int)dir){
case 0:
officeScreen["office_left"].SetTexture(state ? 1 : 0);
break;

View file

@ -0,0 +1,28 @@
using GlobalClassLib;
namespace FNAF_Clone.Map;
public class ClientMapManager {
private static MapTileProjection[,] map = new MapTileProjection[5, 5];
private static MapTileProjection Get((int x, int y) coords) => map[coords.x, coords.y];
public static void InitMap((int id1, int id2, ConnectorType type)[] connectors) {
for (int i = 0; i < 5; i++){
for (int j = 0; j < 2; j++){
map[i, j] = new MapTileProjection(MapTileProjection.CoordsToId(i, j)); // TODO: implement ownership
}
map[i, 2] = new MapTileProjection(MapTileProjection.CoordsToId(i, 2));
for (int j = 3; j < 5; j++){
map[i, j] = new MapTileProjection(MapTileProjection.CoordsToId(i, j));
}
}
foreach (var con in connectors){
(int x, int y) coords1 = MapTileProjection.IdToCoords(con.id1);
(int x, int y) coords2 = MapTileProjection.IdToCoords(con.id2);
map[coords1.x, coords1.y].AddConnector(new TileConnectorProjection(map[coords2.x, coords2.y], con.type));
}
}
public static TileConnectorProjection[] GetConnectors(int tileId) => Get(MapTileProjection.IdToCoords(tileId)).GetAllConnectors();
}

View file

@ -0,0 +1,8 @@
using GlobalClassLib;
namespace FNAF_Clone.Map;
public class MapTileProjection : GlobalMapTile<TileConnectorProjection, MapTileProjection> {
public MapTileProjection(int id) : base(id) {
}
}

View file

@ -0,0 +1,13 @@
using GlobalClassLib;
namespace FNAF_Clone.Map;
public class TileConnectorProjection : GlobalTileConnector<MapTileProjection, TileConnectorProjection> {
public TileConnectorProjection(MapTileProjection tile1, MapTileProjection tile2, ConnectorType type) : base(tile1, tile2, type) {
}
public TileConnectorProjection(MapTileProjection tile2, ConnectorType type) : base(tile2, type) {
}
public override TileConnectorProjection Clone() => new(Tiles.tile1, Tiles.tile2, Type);
}

View file

@ -12,6 +12,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GlobalClassLib\GlobalClassLib.csproj" />
<ProjectReference Include="..\PacketLib\PacketLib.csproj" />
</ItemGroup>

View file

@ -1,4 +1,5 @@
using FNAF_Server.Map;
using GlobalClassLib;
using PacketLib;
namespace FNAF_Server;
@ -6,7 +7,20 @@ namespace FNAF_Server;
public class GameLogic {
public static void Init() {
// Create map
MapManager.InitMap();
//Send map to client
TileConnector[] connectors = MapManager.GetAllConnectors();
int[] connectorsConverted = new int[connectors.Length * 3];
for (int i = 0; i < connectors.Length; i++){
connectorsConverted[i * 3] = connectors[i].Tiles.tile1.Id;
connectorsConverted[i * 3 + 1] = connectors[i].Tiles.tile2.Id;
connectorsConverted[i * 3 + 2] = (int)connectors[i].Type;
}
Server.SendPacketToAll(new MapInitPacket{Connectors = connectorsConverted});
}
public static void Update() {

View file

@ -1,17 +1,19 @@
using GlobalClassLib;
namespace FNAF_Server.Map;
public static class MapManager {
private static MapTile[,] map = new MapTile[5, 5];
private static Dictionary<(int x1, int y1), (int x2, int y2, int value)[]> halfConnectors = new(){
[(0, 0)] =[(1, 0, 1), (0, 1, 1)],
[(1, 0)] =[(2, 0, 1), (1, 1, 1)],
[(3, 0)] =[(2, 0, 1), (4, 0, 1), (3, 1, 1)],
[(4, 0)] =[(4, 1, 1)],
[(0, 1)] =[(1, 1, 1)],
[(1, 1)] =[(1, 2, 1)],
[(2, 1)] =[(2, 2, 1), (2, 0, 1)],
[(3, 1)] =[(3, 2, 1), (4, 1, 1)]
private static Dictionary<(int x1, int y1), (int x2, int y2, int value, ConnectorType type)[]> halfConnectors = new(){
[(0, 0)] =[(1, 0, 1, ConnectorType.HALL), (0, 1, 1, ConnectorType.DOOR_REMOTE)],
[(1, 0)] =[(2, 0, 1, ConnectorType.DOOR_OFFICE), (1, 1, 1, ConnectorType.DOOR_REMOTE)],
[(3, 0)] =[(2, 0, 1, ConnectorType.DOOR_OFFICE), (4, 0, 1, ConnectorType.HALL), (3, 1, 1, ConnectorType.DOOR_REMOTE)],
[(4, 0)] =[(4, 1, 1, ConnectorType.DOOR_REMOTE)],
[(0, 1)] =[(1, 1, 1, ConnectorType.HALL)],
[(1, 1)] =[(1, 2, 1, ConnectorType.DOOR_REMOTE)],
[(2, 1)] =[(2, 2, 1, ConnectorType.HALL), (2, 0, 1, ConnectorType.DOOR_OFFICE)],
[(3, 1)] =[(3, 2, 1, ConnectorType.DOOR_REMOTE), (4, 1, 1, ConnectorType.HALL)]
};
private static Dictionary<(int x1, int y1), (int x2, int y2, int value)[]> mainHallwayConnectors = new(){
@ -24,24 +26,42 @@ public static class MapManager {
public static void InitMap() { // TODO: make map size not hardcoded
for (int i = 0; i < 5; i++){
for (int j = 0; j < 2; j++){
map[i, j] = new MapTile(i * 5 + j, null); // TODO: implement ownership
map[i, j] = new MapTile(MapTile.CoordsToId(i, j), null); // TODO: implement ownership
}
map[i, 2] = new MapTile(i * 5 + 2, null);
map[i, 2] = new MapTile(MapTile.CoordsToId(i, 2), null);
for (int j = 3; j < 5; j++){
map[i, j] = new MapTile(i * 5 + j, null);
map[i, j] = new MapTile(MapTile.CoordsToId(i, j), null);
}
}
foreach (var con in mainHallwayConnectors){
map[con.Key.x1, con.Key.y1].AddConnectors(con.Value.Select(c => (map[c.x2, c.y2], TileConnector.ConnectorType.HALL, c.value)).ToArray());
map[con.Key.x1, con.Key.y1].AddConnectors(con.Value.Select(c => new TileConnector(map[c.x2, c.y2], ConnectorType.HALL, c.value)).ToArray());
}
foreach (var con in halfConnectors){
map[con.Key.x1, con.Key.y1].AddConnectors(con.Value.Select(c => (map[c.x2, c.y2], TileConnector.ConnectorType.HALL, c.value)).ToArray());
map[con.Key.x1, con.Key.y1].AddConnectors(con.Value.Select(c => new TileConnector(map[c.x2, c.y2], c.type, c.value)).ToArray());
}
foreach (var con in halfConnectors){
map[4 - con.Key.x1, 4 - con.Key.y1].AddConnectors(con.Value.Select(c => (map[4 - c.x2, 4 - c.y2], TileConnector.ConnectorType.HALL, c.value)).ToArray());
map[4 - con.Key.x1, 4 - con.Key.y1].AddConnectors(con.Value.Select(c => new TileConnector(map[4 - c.x2, 4 - c.y2], c.type, c.value)).ToArray());
}
}
public static TileConnector[] GetAllConnectors() {
List<TileConnector> connectors = new();
for (int i = 0; i < 5; i++){
for (int j = 0; j < 5; j++){
Array.ForEach(map[i, j].GetAllConnectors(), c => {
if(c.Tiles.tile1.Id < c.Tiles.tile2.Id)
connectors.Add(c);
});
}
}
return connectors.ToArray();
}
}

View file

@ -1,25 +1,44 @@
using System.Net.Security;
using GlobalClassLib;
namespace FNAF_Server.Map;
public class MapTile {
public int Id { get; private set; }
public class MapTile : GlobalMapTile<TileConnector, MapTile> {
public ServerPlayer Owner{ get; private set; }
public bool Lit { get; set; } = false;
private List<TileConnector> Connectors { get; set; } = new();
public MapTile(int id, ServerPlayer owner) {
Id = id;
public MapTile(int id, ServerPlayer owner) : base(id) {
Owner = owner;
}
public void AddConnector(MapTile tile, TileConnector.ConnectorType type, int value) {
Connectors.Add(new TileConnector(this, tile, type, value));
tile.Connectors.Add(new TileConnector(tile, this, type, value));
}
public void AddConnectors((MapTile tile, TileConnector.ConnectorType type, int value)[] connectors) =>
Array.ForEach(connectors, c => AddConnector(c.tile, c.type, c.value));
public override string ToString() => $"{PositionAsString} -> {string.Join(", ", Connectors.Select(c => c.Tiles.Item2.PositionAsString))}";
public string PositionAsString => $"[{Id / 5}, {Id % 5}]"; // for debug purposes
// public int Id { get; private set; }
// public ServerPlayer Owner { get; private set; }
// public bool Lit { get; set; } = false;
//
// private List<TileConnector> connectors = new();
//
// public MapTile(int id, ServerPlayer owner) {
// Id = id;
// Owner = owner;
// }
// public void AddConnector(MapTile tile, TileConnector.ConnectorType type, int value) {
// connectors.Add(new TileConnector(this, tile, type, value));
// tile.connectors.Add(new TileConnector(tile, this, type, value));
//
//
// }
//
// public void AddConnectors((MapTile tile, TileConnector.ConnectorType type, int value)[] connectors) =>
// Array.ForEach(connectors, c => AddConnector(c.tile, c.type, c.value));
//
// public override string ToString() => $"{PositionAsString} -> {string.Join(", ", connectors.Select(c => c.Tiles.Item2.PositionAsString))}";
// public string PositionAsString => $"[{Id}]"; // for debug purposes
//
// public TileConnector? GetConnector(int id) {
// foreach (var con in connectors){
// if (con.Tiles.Item2.Id == id) return con;
// }
//
// return null;
// }
}

View file

@ -1,18 +1,45 @@
using GlobalClassLib;
namespace FNAF_Server.Map;
public struct TileConnector(MapTile tile1, MapTile tile2, TileConnector.ConnectorType type, int value) {
public (MapTile, MapTile) Tiles { get; set; } = (tile1, tile2);
public ConnectorType Type { get; set; } = type;
public bool Blocked { get; set; } = false;
public int Value{ get; set; } = value;
public MapTile OtherTile(MapTile tile) => Tiles.Item1 == tile ? Tiles.Item2 : Tiles.Item1;
public override string ToString() => $"Con ({Tiles.Item1.PositionAsString} -> {Tiles.Item2.PositionAsString})";
public enum ConnectorType {
HALL,
DOOR,
VENT
public class TileConnector : GlobalTileConnector<MapTile,TileConnector> {
public int Value{ get; set; }
public bool Blocked { get; set; }
public TileConnector(MapTile tile1, MapTile tile2, ConnectorType type, int value) : base(tile1, tile2, type) {
Value = value;
}
public TileConnector(MapTile tile2, ConnectorType type, int value) : base(tile2, type) {
Value = value;
}
// private readonly MapTile _tile1;
// private readonly MapTile _tile2;
//
// public TileConnector(MapTile tile1, MapTile tile2, TileConnector.ConnectorType type, int value) {
// _tile1 = tile1;
// _tile2 = tile2;
// Type = type;
// Value = value;
// }
//
// public (MapTile, MapTile) Tiles => (tile1: _tile1, tile2: _tile2);
//
// public ConnectorType Type { get; set; }
// public bool Blocked { get; set; } = false;
// public int Value{ get; set; }
//
// public MapTile OtherTile(MapTile tile) => Tiles.Item1 == tile ? Tiles.Item2 : Tiles.Item1;
//
// public override string ToString() => $"Con ({Tiles.Item1.PositionAsString} -> {Tiles.Item2.PositionAsString})";
// public enum ConnectorType {
// HALL,
// DOOR_REMOTE,
// DOOR_OFFICE,
// VENT
// }
public override TileConnector Clone() => new(Tiles.tile1, Tiles.tile2, Type, Value);
}

View file

@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Diagnostics;
using System.Threading;
using FNAF_Server.Map;
using LiteNetLib;
using LiteNetLib.Utils;
using PacketLib;
@ -116,7 +117,11 @@ public class Server {
P2 = newPlayer;
}
GameLogic.Init(); // TODO: implement a way to start the game once both players join
GameLogic.Init(); // TODO: move this to the condition above to wait for the other player
}

View file

@ -0,0 +1,9 @@
namespace GlobalClassLib;
public enum ConnectorType {
HALL = 0,
DOOR_REMOTE = 1,
DOOR_OFFICE = 2,
VENT = 3
}

View file

@ -0,0 +1,8 @@
namespace GlobalClassLib;
public enum Direction {
EAST = 0,
NORTH = 1,
WEST = 2,
SOUTH = 3
}

View file

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,43 @@
namespace GlobalClassLib;
public abstract class GlobalMapTile<TCon, TTile> where TCon : GlobalTileConnector<TTile, TCon> where TTile : GlobalMapTile<TCon, TTile> { // TTile should be the inheriting class
public int Id { get; private set; }
public bool Lit { get; set; } = false;
private List<TCon> connectors = new();
public GlobalMapTile(int id) {
Id = id;
}
public void AddConnector(TCon connector) { // tile1 is ignored when provided
connector.Tiles.tile1 = (TTile)this;
connectors.Add(connector);
connector.Tiles.tile2._AddConnectorNoRepeat(connector.Clone());
// connectors.Add(new TCon(this, tile, type));
// tile.connectors.Add(new GlobalTileConnector(tile, this, type));
}
private void _AddConnectorNoRepeat(TCon connector) {
(connector.Tiles.tile1, connector.Tiles.tile2) = (connector.Tiles.tile2, connector.Tiles.tile1);
connectors.Add(connector);
}
public void AddConnectors(TCon[] connectors) =>
Array.ForEach(connectors, AddConnector);
public override string ToString() => $"{PositionAsString} -> {string.Join(", ", connectors.Select(c => c.Tiles.Item2.PositionAsString))}";
public string PositionAsString => $"[{Id}]"; // for debug purposes
public TCon? GetConnector(int id) {
foreach (var con in connectors){
if (con.Tiles.Item2.Id == id) return con;
}
return null;
}
public TCon[] GetAllConnectors() => connectors.ToArray();
public static int CoordsToId(int x, int y) => x * 5 + y;
public static (int, int) IdToCoords(int id) => (id % 5, id / 5);
}

View file

@ -0,0 +1,28 @@
namespace GlobalClassLib;
public abstract class GlobalTileConnector<TTile, TCon> where TTile : GlobalMapTile<TCon,TTile> where TCon : GlobalTileConnector<TTile, TCon> {
// private readonly TTile _tile1;
// private readonly TTile _tile2;
public GlobalTileConnector(TTile tile1, TTile tile2, ConnectorType type) {
Tiles.tile1 = tile1;
Tiles.tile2 = tile2;
Type = type;
}
public GlobalTileConnector(TTile tile2, ConnectorType type) {
Tiles.tile2 = tile2;
Type = type;
}
public (TTile tile1, TTile tile2) Tiles;
public (int, int) Id => (Tiles.tile1.Id, Tiles.tile2.Id);
public ConnectorType Type { get; set; }
public TTile OtherTile(TTile tile) => Tiles.Item1 == tile ? Tiles.Item2 : Tiles.Item1;
public override string ToString() => $"Con ({Tiles.Item1.PositionAsString} -> {Tiles.Item2.PositionAsString})";
public abstract TCon Clone();
}

View file

@ -0,0 +1,6 @@
namespace PacketLib;
public class MapInitPacket {
public int[] Connectors { get; set; } // triplets (tile1 id, tile2 id, type)
}

View file

@ -9,4 +9,5 @@ public static class NetDataWriterExtensions {
gevent.Deserialize(reader);
return gevent;
}
}

View file

@ -10,4 +10,8 @@
<PackageReference Include="LiteNetLib" Version="1.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GlobalClassLib\GlobalClassLib.csproj" />
</ItemGroup>
</Project>

View file

@ -1,3 +1,4 @@
using GlobalClassLib;
using LiteNetLib.Utils;
namespace PacketLib;
@ -5,7 +6,8 @@ namespace PacketLib;
public struct PlayerCommand : INetSerializable {
public static PlayerCommand SWITCH_CAM(int idx, int idy) => new(){ID = 0, Args = [idx, idy] };
public static PlayerCommand TOGGLE_MONITOR() => new(){ID = 1, Args = []};
public static PlayerCommand TOGGLE_DOOR_OFFICE(int doorId) => new(){ID = 2, Args = [doorId]};
public static PlayerCommand TOGGLE_DOOR_OFFICE(Direction direction) => new(){ID = 2, Args = [(int)direction]};
public static PlayerCommand TOGGLE_DOOR_REMOTE(int remoteDoorId) => new(){ID = 3, Args = [remoteDoorId]};
public int ID{ get; set; }
public bool Hideable => ID < 0;
public int[] Args{ get; private set; }