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

@ -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
@ -58,21 +60,30 @@ public class Client {
public static void SendCommands(PlayerCommand[] pCommands) {
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;
@ -44,21 +46,32 @@ public class UIManager {
monitorScreen.AddElement("view-frame", new UIElement(monitorAtlas[1], new Point(62, 55)));
monitorScreen.AddElement("map-frame", new UIElement(monitorAtlas[2], new Point(334, 135)));
monitorScreen.AddElement("map", new UIElement(monitorAtlas[3], new Point(334, 135)));
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++){
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);
}