Hlavní menu, synchronizace jmen hráčů. Client hru spustí až ve chvíli kdy dostane správný packet. Oprava bugu v se scalováním UIElementu

This commit is contained in:
Perry 2026-03-11 22:35:30 +01:00
parent e6128dc9f5
commit 7656707177
19 changed files with 315 additions and 53 deletions

View file

@ -5,11 +5,13 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADictionary_00602_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5e56f40aa42e4fc4b94786ec57da7544d1a400_003F07_003F804b4506_003FDictionary_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F419320464d0849fb89a7e518f7ff8bc978a00_003Fe8_003F1faf66e3_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEventChannel_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F384e456bc26f45f5a6a6a20ae50c6e0dd1a400_003Ffb_003Fca10f160_003FEventChannel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGame_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa69b121f85054691b2a8d28643003c23136600_003Fc7_003F517f8541_003FGame_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AINetSerializable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3d47105f47a240929625d0f531812b9e1c000_003F0f_003Fe2eeb2cd_003FINetSerializable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKeyValuePair_00602_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F384e456bc26f45f5a6a6a20ae50c6e0dd1a400_003Fe8_003F7272ae72_003FKeyValuePair_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AList_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5e56f40aa42e4fc4b94786ec57da7544d1a400_003Fe4_003F9c3fcf71_003FList_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANetManager_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3d47105f47a240929625d0f531812b9e1c000_003Fd6_003Fec041615_003FNetManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANetSerializer_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3d47105f47a240929625d0f531812b9e1c000_003Fcc_003F8a34584a_003FNetSerializer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThread_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5e56f40aa42e4fc4b94786ec57da7544d1a400_003Fb3_003F92670209_003FThread_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5e56f40aa42e4fc4b94786ec57da7544d1a400_003Ffb_003Fa0fd6fc3_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATitleContainer_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa69b121f85054691b2a8d28643003c23136600_003F62_003F374a72ad_003FTitleContainer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AValueTuple_00602_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5e56f40aa42e4fc4b94786ec57da7544d1a400_003F09_003F312b9770_003FValueTuple_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View file

@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using FNAF_Clone.GUI;
using FNAF_Clone.Map;
using GlobalClassLib;
@ -12,6 +13,20 @@ using PacketLib;
namespace FNAF_Clone;
public class Client {
public enum ConnectionState {
IDLE,
CONNECTING,
CONNECTED,
ACCEPTED,
GAME_STARTING,
GAME_IN_PROGRESS,
SERVER_NOT_FOUND,
SERVER_FULL,
ERROR
}
public static ConnectionState State { get; private set; } = ConnectionState.IDLE;
private static EventBasedNetListener listener = new();
private static NetManager client;
private static NetPeer server;
@ -24,6 +39,7 @@ public class Client {
public static ClientPlayer GetPlayer(int pid) => Player.state.pid == pid ? Player : Opponent;
public static void Connect(string endPoint, int port) {
State = ConnectionState.CONNECTING;
writer = new NetDataWriter();
processor = new NetPacketProcessor();
@ -32,7 +48,11 @@ public class Client {
processor.SubscribeReusable<JoinAcceptPacket>(OnJoinAccept);
processor.SubscribeReusable<UpdatePlayerPacket>(OnPlayerUpdate);
processor.SubscribeReusable<MapInitPacket>(OnMapInit);
processor.SubscribeReusable<OpponentInitPacket>(packet => Opponent.state = packet.state);
processor.SubscribeReusable<OpponentInitPacket>(packet => { // TODO: move this to a method
Opponent.state = packet.state;
Opponent.username = packet.username;
State = ConnectionState.GAME_STARTING;
});
client = new NetManager(listener){
AutoRecycle = true
@ -48,10 +68,11 @@ public class Client {
listener.PeerConnectedEvent += peer => {
Console.WriteLine("Connected to Server");
server = peer;
SendPacket(new JoinPacket {username = "Player1"}, DeliveryMethod.ReliableOrdered);
State = ConnectionState.CONNECTED;
SendPacket(new JoinPacket {username = Player.username == "" ? "Anonymous" : Player.username}, DeliveryMethod.ReliableOrdered);
};
client.Connect(endPoint, port, ""); // TODO: figure out how keys work
new Thread(() => client.Connect(endPoint, port, "")).Start(); // TODO: figure out how keys work
}
public static void SendPacket<T>(T packet, DeliveryMethod deliveryMethod) where T : class, new() {
@ -71,6 +92,7 @@ public class Client {
private static void OnJoinAccept(JoinAcceptPacket packet) {
Console.WriteLine($"Accepted by server, pid: {packet.state.pid}");
State = ConnectionState.ACCEPTED;
Player.state = packet.state;
}
@ -83,8 +105,7 @@ public class Client {
TileConnectorProjection[] connectors = connectorsData.Select(c => new TileConnectorProjection(ClientMapManager.Get(c.id1), ClientMapManager.Get(c.id2), c.type)).ToArray();
ClientMapManager.InitConnectors(connectors);
UIManager.InitUI();
UIManager.SpawnDoors(connectors.Where(c => c.Type == ConnectorType.DOOR_REMOTE).ToArray());
UIManager.SpawnMapElements(connectors.Where(c => c.Type == ConnectorType.DOOR_REMOTE).ToArray());
}

View file

@ -25,7 +25,7 @@ public class CommandManager {
Array.ForEach(keybinds, tuple => InputManager.AddListener(tuple.label, tuple.key, () => tuple.action(), InputTiming.PRESS, allControlsHook));
}
public static void AllowInput(bool state) {
public static void AllowGameControls(bool state) {
allControlsHook.Enabled = state;
}

View file

@ -17,7 +17,7 @@ with.
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>20</Size>
<Size>40</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change

View file

@ -101,6 +101,11 @@ public class EventProcessor {
if(Client.Player.state.pid == e.Args[0]) UIManager.ShowVictoryScreen();
break;
case 12:
Console.WriteLine($"E: Game started");
UIManager.DisplayGameUI();
UIManager.StartTimer();
break;
}
}
}

View file

@ -0,0 +1,37 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace FNAF_Clone.GUI;
public class LoadingUIElement : TextUIElement {
private string expectedEndpoint;
private Client.ConnectionState lastState = Client.ConnectionState.IDLE;
public LoadingUIElement(Point corner1, SpriteFont font, string expectedEndpoint, Alignment alignment = Alignment.CENTER, bool autoBounds = true) : base(corner1, font, alignment, autoBounds) {
this.expectedEndpoint = expectedEndpoint;
Active = true;
// Color = Color.LightGray;
}
public override void Update() {
if(lastState == Client.State) return;
lastState = Client.State;
switch (Client.State){
case Client.ConnectionState.CONNECTING:
Text = "Connecting to " + expectedEndpoint;
break;
case Client.ConnectionState.CONNECTED:
Text = "Connected to " + expectedEndpoint;
break;
case Client.ConnectionState.ACCEPTED:
Text = "Waiting for opponent...";
break;
case Client.ConnectionState.GAME_STARTING:
Text = "Opponent: " + Client.Opponent.username;
Color = Color.White;
// ScaleMultiplier = 1.5f;
break;
}
}
}

View file

@ -0,0 +1,45 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGameLibrary.Graphics;
namespace FNAF_Clone.GUI;
public class MenuInputField : UIElement {
private TextUIElement labelElement;
private TextBoxUIElement textBoxElement;
public MenuInputField(SpriteFont font, Point position, string label, string defaultValue = "") : base(position, position) {
labelElement =
new TextUIElement(position, font){Text = label, Color = Color.Gray};
textBoxElement =
new TextBoxUIElement(font,
new(labelElement.Bounds.Item1.X + (int)labelElement.Measure().X, labelElement.Bounds.Item1.Y),
new(640, labelElement.Bounds.Item2.Y + (int)labelElement.Measure().Y));
textBoxElement.OnFocused = () => {
textBoxElement.Color = Color.LightGreen;
labelElement.Color = Color.DarkGreen;
};
textBoxElement.OnUnfocused = () => {
textBoxElement.Color = Color.White;
labelElement.Color = Color.Gray;
};
Bounds = (labelElement.Bounds.Item1, textBoxElement.Bounds.Item2);
Pressable = true;
OnMousePress = textBoxElement.OnMousePress;
textBoxElement.Text = defaultValue;
}
public string Text => textBoxElement.Text;
public override void Draw(SpriteBatch spriteBatch) {
labelElement.Draw(spriteBatch);
textBoxElement.Draw(spriteBatch);
}
public override void Update() {
base.Update();
labelElement.Update();
textBoxElement.Update();
}
}

View file

@ -4,4 +4,6 @@ namespace FNAF_Clone.GUI;
public static class PointExtensions {
public static Point MultiplyByScalar(this Point point, int scalar) => new(point.X * scalar, point.Y * scalar);
public static Point MultiplyByScalar(this Point point, float scalar) => new((int)(point.X * scalar), (int)(point.Y * scalar));
}

View file

@ -27,9 +27,8 @@ public class Screen {
if (CurrentScreen.temporary){
Screens.Remove(CurrentScreen.Label);
}
else if (CurrentScreen != null){
CurrentScreen.Active = false;
}
CurrentScreen.Active = false;
CurrentScreen = Screens[id];
CurrentScreen.Active = true;
}
@ -40,14 +39,20 @@ public class Screen {
public static void UpdateAll() {
foreach (var screen in Screens.Values){
// if (!screen.Active) continue;
if (!screen.Active) continue;
screen.Update();
}
}
public static Screen Empty => new(""){temporary = true};
public static void SetOverlayScreen(string id) {
if (OverlayScreen.temporary){
Screens.Remove(OverlayScreen.Label);
}
OverlayScreen.Active = false;
OverlayScreen = Screens[id];
OverlayScreen.Active = true;
}
public static void DisableOverlay() {
@ -80,8 +85,9 @@ public class Screen {
public UIElement this[string id] => elements[id];
public UIElement TryGetElement(string id) => elements.TryGetValue(id, out var val) ? val : null;
public void AddElement(string id, UIElement element) {
public UIElement AddElement(string id, UIElement element) {
elements.Add(id, element);
return element;
}
public void SetActive(bool active) {

View file

@ -0,0 +1,42 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGameLibrary;
using MonoGameLibrary.Graphics;
using MonoGameLibrary.Input;
namespace FNAF_Clone.GUI;
public class TextBoxUIElement : TextUIElement {
public bool Focused{ get; set; } = false;
public Action OnFocused{ get; set; } = () => { };
public Action OnUnfocused{ get; set; } = () => { };
public TextBoxUIElement(SpriteFont font, Point corner1, Point corner2) : base(corner1, corner2, font) {
Core.Instance.Window.TextInput += TextInputHandler;
InputManager.AddListener(InputManager.MouseButton.LEFT, () => {
if (!IsWithinBounds(InputManager.MouseState.Position)){
Focused = false;
OnUnfocused();
}
},
InputTiming.PRESS, new InputListenerHook(true));
Pressable = true;
OnMousePress = () => {
Focused = true;
OnFocused();
};
}
public void TextInputHandler(object sender, TextInputEventArgs e) {
if (!Focused) return;
if (e.Character == '\b') {
if (Text.Length > 0) Text = Text[..^1];
return;
}
if(Font.Characters.Contains(e.Character))
Text += e.Character;
}
}

View file

@ -7,11 +7,28 @@ namespace FNAF_Clone.GUI;
public class TextUIElement : UIElement {
public SpriteFont Font { get; set; }
public string Text{ get; set; } = "";
public Color Color{ get; set; } = Color.White;
private Vector2 origin;
public Alignment CurrentAlignment { get; set; }
public TextUIElement(Point corner1, SpriteFont font, Alignment alignment = Alignment.LEFT) : base(corner1, corner1) {
public string Text{
get;
set{
field = value;
if(AutoBounds)
Bounds = (Bounds.Item1, Bounds.Item1 + new Point((int)Measure().X, (int)Measure().Y));
}
} = "";
public Color Color{ get; set; } = Color.White;
public bool AutoBounds{ get; protected set; } = false;
private Vector2 origin;
private const float UNIVERSAL_TEXT_SCALE_MULTIPLIER = 0.3f;
public TextUIElement(Point corner1, SpriteFont font, Alignment alignment = Alignment.LEFT, bool autoBounds = true) : base(corner1, corner1) {
Font = font;
AutoBounds = autoBounds;
Align(alignment);
}
public TextUIElement(Point corner1, Point corner2, SpriteFont font, Alignment alignment = Alignment.LEFT) : base(corner1, corner2) {
Font = font;
Align(alignment);
}
@ -19,10 +36,11 @@ public class TextUIElement : UIElement {
public override void Draw(SpriteBatch spriteBatch) {
base.Draw(spriteBatch);
align();
spriteBatch.DrawString(Font, Text, screenSpaceBounds.Item1.ToVector2(), Color, 0, origin, ScaleMultiplier, SpriteEffects.None, 0);
spriteBatch.DrawString(Font, Text, screenSpaceBounds.Item1.ToVector2(), Color, 0, origin, pixelScaleMultiplier * UNIVERSAL_TEXT_SCALE_MULTIPLIER, SpriteEffects.None, 0);
}
public void Align(Alignment alignment) {
CurrentAlignment = alignment;
switch (alignment){
case Alignment.LEFT:
AlignLeft();
@ -36,14 +54,23 @@ public class TextUIElement : UIElement {
}
}
protected override void UpdateBounds() {
Point inSpaceOrigin = Bounds.Item1 + origin.ToPoint();
_bounds = ((Bounds.Item1 - inSpaceOrigin).MultiplyByScalar(ScaleMultiplier) + inSpaceOrigin, (Bounds.Item2 - Bounds.Item1).MultiplyByScalar(ScaleMultiplier) + inSpaceOrigin);
screenSpaceBounds = (Bounds.Item1.MultiplyByScalar(pixelScaleMultiplier), Bounds.Item2.MultiplyByScalar(pixelScaleMultiplier));
Align(CurrentAlignment);
}
public Vector2 Measure() => Font.MeasureString(Text) * UNIVERSAL_TEXT_SCALE_MULTIPLIER * ScaleMultiplier;
private void AlignLeft() {
align = () => origin = Vector2.Zero;
}
private void AlignRight() {
align = () => origin = Font.MeasureString(Text);
align = () => origin = Font.MeasureString(Text) * ScaleMultiplier;
}
private void AlignCenter() {
align = () => origin = Font.MeasureString(Text) / 2;
align = () => origin = new(Font.MeasureString(Text).X * ScaleMultiplier / 2, 0);
}
private Action align;

View file

@ -9,7 +9,7 @@ public class TimerUIElement : TextUIElement{
private Stopwatch stopwatch = new();
public TimerUIElement(Point corner1, SpriteFont font) : base(corner1, font) {
public TimerUIElement(Point corner1, SpriteFont font) : base(corner1, corner1, font) {
Text = "00:00.000";
}

View file

@ -14,10 +14,11 @@ public class UIElement {
public bool Pressable { get; set; } = false;
public bool Visible { get; set; } = true;
protected (Point, Point) _bounds;
public (Point, Point) Bounds{
get;
private set { field = value; UpdateBounds(); }
} // TODO: Change this to support non-rectangular hitboxes
get => _bounds;
protected set { _bounds = value; UpdateBounds(); }
}
protected (Point, Point) screenSpaceBounds;
public List<TextureRegion> Textures = new();
@ -33,13 +34,14 @@ public class UIElement {
LoadPixelScaleMultiplier();
}
}
private int pixelScaleMultiplier = 1;
protected int pixelScaleMultiplier = 1;
private void LoadPixelScaleMultiplier() {
pixelScaleMultiplier = (int)(UIManager.GlobalPixelMultiplier * _scaleMultiplier); // TODO: move GlobalPixelMultiplier somewhere where it would make sense
UpdateBounds();
}
private void UpdateBounds() {
protected virtual void UpdateBounds() {
_bounds = (Bounds.Item1, (Bounds.Item2 - Bounds.Item1).MultiplyByScalar(ScaleMultiplier) + Bounds.Item1);
screenSpaceBounds = (Bounds.Item1.MultiplyByScalar(pixelScaleMultiplier), Bounds.Item2.MultiplyByScalar(pixelScaleMultiplier));
}

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using FNAF_Clone.Map;
using GlobalClassLib;
using Microsoft.Xna.Framework;
@ -20,6 +21,8 @@ public class UIManager {
public const string OVERLAY = "overlay";
public const string WIN = "win";
public const string LOSE = "lose";
public const string MENU = "menu";
public const string LOADING = "loading";
}
private static Screen officeScreen = new(ScreenTypes.OFFICE);
@ -27,6 +30,8 @@ public class UIManager {
private static Screen overlayScreen = new(ScreenTypes.OVERLAY);
private static Screen winScreen = new(ScreenTypes.WIN);
private static Screen loseScreen = new(ScreenTypes.LOSE);
private static Screen menuScreen = new(ScreenTypes.MENU);
private static Screen loadingScreen = new(ScreenTypes.LOADING);
private static TextureAtlas testAtlas;
public static TextureAtlas OfficeAtlas{ get; private set; }
@ -42,18 +47,20 @@ public class UIManager {
private static InputListenerHook monitorSwitchHook;
public static void InitUI() {
GlobalPixelMultiplier = Core.graphicsDevice.Viewport.Height / 360;
testAtlas = TextureAtlas.FromFile(Core.content, "images/testBlocks-definition.xml"); // TODO: move this to its own method
public static void LoadAssets() {
testAtlas = TextureAtlas.FromFile(Core.content, "images/testBlocks-definition.xml");
OfficeAtlas = TextureAtlas.FromFile(Core.content, "images/office-definition.xml");
MonitorAtlas = TextureAtlas.FromFile(Core.content, "images/monitor-definition.xml");
EnemyAtlas = TextureAtlas.FromFile(Core.content, "images/enemies-definition.xml");
PixelMonoFont = Core.content.Load<SpriteFont>("ponderosa");
}
Screen.AddScreens([officeScreen, monitorScreen, overlayScreen, winScreen, loseScreen]);
Screen.SetScreen(ScreenTypes.OFFICE);
Screen.SetOverlayScreen(ScreenTypes.OVERLAY);
public static void InitUI() {
GlobalPixelMultiplier = Core.graphicsDevice.Viewport.Height / 360;
Screen.AddScreens([officeScreen, monitorScreen, overlayScreen, winScreen, loseScreen, menuScreen, loadingScreen]);
// Screen.SetScreen(ScreenTypes.OFFICE);
// Screen.SetOverlayScreen(ScreenTypes.OVERLAY);
officeScreen.AddElement("office_left", new UIElement([OfficeAtlas[3], OfficeAtlas[0]], Point.Zero));
officeScreen.AddElement("office_centre", new UIElement([OfficeAtlas[4], OfficeAtlas[1]], new Point(200, 0)));
@ -69,6 +76,74 @@ 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)));
timerElement = new(new(0, 0), PixelMonoFont);
overlayScreen.AddElement("timer", timerElement);
winScreen.AddElement("win-text", new TextUIElement(new(320, 180), PixelMonoFont, TextUIElement.Alignment.CENTER){Text = "YOU WIN", Color = Color.Green});
loseScreen.AddElement("lose-text", new TextUIElement(new(320, 180), PixelMonoFont, TextUIElement.Alignment.CENTER){Text = "YOU LOSE", Color = Color.Red});
// overlayScreen.AddElement("test", new TextBoxUIElement(PixelMonoFont, Point.Zero, new(200, 100)));
// Main menu
// TextUIElement serverIpLabel = (TextUIElement) menuScreen.AddElement("server-ip-label", new TextUIElement(new (20, 20), PixelMonoFont){Text = "ENTER SERVER IP: ", Color = Color.Gray});
// TextBoxUIElement serverIpTextBox = (TextBoxUIElement)menuScreen.AddElement("server-ip-textbox",
// new TextBoxUIElement(PixelMonoFont,
// new(serverIpLabel.Bounds.Item1.X + (int)serverIpLabel.Measure().X, serverIpLabel.Bounds.Item1.Y),
// new(640, serverIpLabel.Bounds.Item2.Y + (int)serverIpLabel.Measure().Y)));
// serverIpTextBox.OnFocused = () => {
// serverIpTextBox.Color = Color.LightGreen;
// serverIpLabel.Color = Color.DarkGreen;
// };
// serverIpTextBox.OnUnfocused = () => {
// serverIpTextBox.Color = Color.White;
// serverIpLabel.Color = Color.Gray;
// };
MenuInputField usernameField = (MenuInputField)menuScreen.AddElement("username-field", new MenuInputField(PixelMonoFont, new(20, 20), "USERNAME: "));
MenuInputField field = (MenuInputField)menuScreen.AddElement("server-ip-field", new MenuInputField(PixelMonoFont, new(usernameField.Bounds.Item1.X, usernameField.Bounds.Item2.Y + 20), "SERVER IP: ", "127.0.0.1"));
UIElement connectButton = menuScreen.AddElement("server-ip-submit", new TextUIElement(new Point(field.Bounds.Item1.X, field.Bounds.Item2.Y), PixelMonoFont)
{
Text = "CONNECT",
Pressable = true,
OnMousePress = () => {
// string[] input = serverIpTextBox.Text.Split(":");
// if(input.Length != 2 || !int.TryParse(input[0], out var port)) return;
// Client.Connect(input[0], port);
Client.Player.username = usernameField.Text;
Client.Connect(field.Text, 9012);
Screen.SetScreen(ScreenTypes.LOADING);
}
});
menuScreen.AddElement("host-button",
new TextUIElement(new(connectButton.Bounds.Item1.X, connectButton.Bounds.Item2.Y + 30), PixelMonoFont) {Text = "HOST"});
loadingScreen.AddElement("loading-text", new LoadingUIElement(new(320, 180), PixelMonoFont, field.Text));
}
public static void DisplayMainMenu() {
Screen.SetScreen(ScreenTypes.MENU);
CommandManager.AllowGameControls(false);
}
public static void DisplayGameUI() {
Screen.SetScreen(ScreenTypes.OFFICE);
Screen.SetOverlayScreen(ScreenTypes.OVERLAY);
CommandManager.AllowGameControls(true);
UpdateCameras([Client.Player.state.camera]); // in case there is an enemy on the default camera
}
public static void StartTimer() {
timerElement.Start();
}
public static void SpawnMapElements(TileConnectorProjection[] doors) {
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;
@ -87,17 +162,6 @@ public class UIManager {
monitorScreen.AddElement("eye-player", new UIElement(MonitorAtlas[24], monitorScreen["room"+Client.Player.state.camera].Bounds.Item1));
monitorScreen.AddElement("eye-opponent", new UIElement([MonitorAtlas[23], MonitorAtlas[22]], monitorScreen["room"+Client.Opponent.state.camera].Bounds.Item1));
timerElement = new(new(0, 0), PixelMonoFont);
overlayScreen.AddElement("timer", timerElement);
timerElement.Start();
winScreen.AddElement("win-text", new TextUIElement(new(320, 180), PixelMonoFont, TextUIElement.Alignment.CENTER){Text = "YOU WIN", Color = Color.Green});
loseScreen.AddElement("lose-text", new TextUIElement(new(320, 180), PixelMonoFont, TextUIElement.Alignment.CENTER){Text = "YOU LOSE", Color = Color.Red});
UpdateCameras([Client.Player.state.camera]);
}
public static void SpawnDoors(TileConnectorProjection[] doors) {
foreach (var door in doors){
if(door.Type != ConnectorType.DOOR_REMOTE) continue;
@ -214,13 +278,13 @@ public class UIManager {
public static void ShowVictoryScreen() {
Screen.SetScreen(ScreenTypes.WIN);
Screen.DisableOverlay();
CommandManager.AllowInput(false);
CommandManager.AllowGameControls(false);
}
public static void ShowDeathScreen() {
Screen.SetScreen(ScreenTypes.LOSE);
Screen.DisableOverlay();
CommandManager.AllowInput(false);
CommandManager.AllowGameControls(false);
}

View file

@ -14,18 +14,21 @@ public class GameMain() : Core("fnafkooo", 1280, 720, false) {
// private GraphicsDeviceManager _graphics;
// private SpriteBatch _spriteBatch;
protected override void Initialize() {
Client.Connect("127.0.0.1", 9012);
// Client.Connect("127.0.0.1", 9012);
CommandManager.InitInputListeners();
// InputManager.AddListener(InputManager.MouseButton.LEFT, (() => Console.WriteLine("LMB pressed at: " + InputManager.MouseState.Position)), InputTiming.PRESS, new InputListenerHook(true));
base.Initialize();
// UIManager.InitUI();
UIManager.InitUI();
UIManager.DisplayMainMenu();
}
protected override void LoadContent() {
UIManager.LoadAssets();
// spriteBatch = new SpriteBatch(GraphicsDevice);
// font = Content.Load<SpriteFont>("font");
}
@ -40,6 +43,8 @@ public class GameMain() : Core("fnafkooo", 1280, 720, false) {
InputManager.NextInputCycle();
Screen.UpdateAll();
if(Client.State == Client.ConnectionState.IDLE) return;
Client.Update();
base.Update(gameTime);
}

View file

@ -34,11 +34,13 @@ public class GameLogic {
Server.SendPacket(new MapInitPacket{Connectors = connectorsConverted, UpsideDown = false}, Server.P1.peer);
Server.SendPacket(new MapInitPacket{Connectors = connectorsConverted, UpsideDown = true}, Server.P2.peer);
EnemyManager.AddEnemy(new LurkEnemy(10)).Spawn(MapManager.Get(12));
EnemyManager.AddEnemy(new LurkEnemy(0)).Spawn(MapManager.Get(12));
EnemyManager.AddEnemy(new NekoEnemy(0)).Spawn(MapManager.Get(2));
EnemyManager.AddEnemy(new SpotEnemy(0)).Spawn(MapManager.Get(12));
EnemyManager.AddEnemy(new SpotEnemy(10)).Spawn(MapManager.Get(12));
Thread.Sleep(3000);
secondCycleTimer.Start();
Server.SendUpdateToAll([GameEvent.GAME_START()]);
}
public static void Update() {
if(secondCycleTimer.Check()) NSecondUpdate = true;

View file

@ -123,8 +123,8 @@ public class Server {
SendPacket(new JoinAcceptPacket { state = newPlayer.state }, peer);
P2 = newPlayer;
SendPacket(new OpponentInitPacket{state = newPlayer.state}, P1.peer);
SendPacket(new OpponentInitPacket{state = P1.state}, P2.peer);
SendPacket(new OpponentInitPacket{state = newPlayer.state, username = newPlayer.username}, P1.peer);
SendPacket(new OpponentInitPacket{state = P1.state, username = P1.username}, P2.peer);
GameLogic.Init();
}

View file

@ -20,6 +20,7 @@ public struct GameEvent : INetSerializable{
public static GameEvent SPOT_SET_ACTIVE(int enemyId, bool state) => new(){ ID = 10, Args = [enemyId, state ? 1 : 0] };
public static GameEvent PLAYER_WIN(int pid) => new(){ID = 11, Args = [pid]};
public static GameEvent GAME_START() => new(){ID = 12};
public int ID{ get; set; }
public bool Hideable => ID < 0;

View file

@ -2,4 +2,5 @@ namespace PacketLib;
public class OpponentInitPacket {
public PlayerState state { get; set; }
public string username { get; set; }
}