Renderování textu, jumpscary, win a lose screen

This commit is contained in:
Perry 2026-03-09 20:05:21 +01:00
parent 9bfe63a166
commit e6128dc9f5
21 changed files with 360 additions and 84 deletions

View file

@ -6,11 +6,12 @@ using MonoGameLibrary.Graphics;
namespace FNAF_Clone;
public class ClientEnemy : GlobalEnemy<MapTileProjection, TileConnectorProjection> {
public ClientEnemy(int typeId, string name, int id, UIElement sprite, int difficulty, MapTileProjection location) : base(difficulty, id) {
public ClientEnemy(int typeId, string name, int id, UIElement sprite, int difficulty, MapTileProjection location, JumpscareUIElement jumpscareSprite) : base(difficulty, id) {
Name = name;
TypeId = typeId;
Sprite = sprite;
Location = location;
JumpscareSprite = jumpscareSprite;
}
// public TextureRegion Sprite { get; set; }
@ -18,4 +19,6 @@ public class ClientEnemy : GlobalEnemy<MapTileProjection, TileConnectorProjectio
public UIElement Sprite { get; set; }
public override string Name{ get; }
public override int TypeId{ get; }
public JumpscareUIElement JumpscareSprite { get; set; }
}

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
@ -8,28 +9,50 @@ using Microsoft.Xna.Framework;
namespace FNAF_Clone;
public class ClientEnemyManager {
public static class ClientEnemyManager {
private static Dictionary<int, ClientEnemy> enemies = new();
private static Point cameraCorner = new Point(64, 64);
private static Action defaultAfterJumpscare = UIManager.ShowDeathScreen;
public static void AddEnemy(ClientEnemy enemy) {
enemies.Add(enemy.Id, enemy);
UIManager.AddEnemySprite(enemy.Id, enemy.Sprite);
UIManager.AddEnemySprite(enemy.Id, enemy.Sprite, enemy.JumpscareSprite);
}
public static void AddEnemyByTemplate(EnemyType type, int id, int difficulty, MapTileProjection location) {
switch (type){
case EnemyType.LURK:
AddEnemy(new ClientEnemy((int)type, "Lurk", id, new UIElement(UIManager.enemyAtlas[0], cameraCorner), difficulty, location));
AddEnemy(new ClientEnemy(
(int)type,
"Lurk",
id,
new UIElement(UIManager.EnemyAtlas[0], cameraCorner),
difficulty,
location,
new JumpscareUIElement(UIManager.EnemyAtlas[0], new(0, 0), 3, 2, 2, 0.1f, afterStop:defaultAfterJumpscare)));
break;
case EnemyType.NEKO:
AddEnemy(new ClientEnemy((int)type, "Neko", id, new UIElement(UIManager.enemyAtlas[1], cameraCorner), difficulty, location));
AddEnemy(new ClientEnemy(
(int)type,
"Neko",
id,
new UIElement(UIManager.EnemyAtlas[1], cameraCorner),
difficulty,
location,
new JumpscareUIElement(UIManager.EnemyAtlas[1], new(0, -30), 3, 2, 2, 0.1f, afterStop:defaultAfterJumpscare)));
break;
case EnemyType.SPOT:
UIElement element =
new UIElement([UIManager.enemyAtlas[2], UIManager.enemyAtlas[3], UIManager.enemyAtlas[4], UIManager.enemyAtlas[5]], cameraCorner);
new UIElement([UIManager.EnemyAtlas[2], UIManager.EnemyAtlas[3], UIManager.EnemyAtlas[4], UIManager.EnemyAtlas[5]], cameraCorner);
element.SetTexture(2);
AddEnemy(new ClientEnemy((int)type, "Spot", id, element, difficulty, location));
AddEnemy(new ClientEnemy(
(int)type,
"Spot",
id,
element,
difficulty,
location,
new JumpscareUIElement(UIManager.EnemyAtlas[2], new(0, 0), 3, 2, 2, 0.1f, afterStop:defaultAfterJumpscare)));
break;
}
}

View file

@ -19,10 +19,14 @@ public class CommandManager {
("Toggle back door", Keys.S, ToggleDoorBack)
];
private static InputListenerHook toggleCamHook = new(true);
private static InputListenerHook allControlsHook{ get; } = new(true);
public static void InitInputListeners() {
Array.ForEach(keybinds, tuple => InputManager.AddListener(tuple.label, tuple.key, () => tuple.action(), InputTiming.PRESS, toggleCamHook));
Array.ForEach(keybinds, tuple => InputManager.AddListener(tuple.label, tuple.key, () => tuple.action(), InputTiming.PRESS, allControlsHook));
}
public static void AllowInput(bool state) {
allControlsHook.Enabled = state;
}
private static void SendToggleCamera() {

View file

@ -73,3 +73,10 @@
#begin images/testBlocks-definition.xml
/copy:images/testBlocks-definition.xml
#begin ponderosa.spritefont
/importer:FontDescriptionImporter
/processor:FontDescriptionProcessor
/processorParam:PremultiplyAlpha=True
/processorParam:TextureFormat=Compressed
/build:ponderosa.spritefont

Binary file not shown.

View file

@ -11,13 +11,13 @@ with.
<!--
Modify this string to change the font that will be imported.
-->
<FontName>Arial</FontName>
<FontName>Ponderosa</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>12</Size>
<Size>20</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change

View file

@ -77,6 +77,13 @@ public class EventProcessor {
UIManager.UpdateCameras([oldPos, e.Args[1]]);
break;
case 8: // attack
Console.WriteLine($"E: Enemy {e.Args[0]} attacked player {e.Args[1]}"); // TODO: add an arg to indicate lethality
if (e.Args[1] == Client.Player.state.pid) {
UIManager.Jumpscare(ClientEnemyManager.Get(e.Args[0]));
}
break;
case 9:
Console.WriteLine($"E: Enemy {e.Args[0]} reset to {e.Args[1]}");
int preResetPos = ClientEnemyManager.Get(e.Args[0]).Location!.Id;
@ -88,6 +95,12 @@ public class EventProcessor {
Console.WriteLine($"E: Spot:{e.Args[0]} turned {(e.Args[1] == 1 ? "on" : " off")}");
ClientEnemyManager.Get(e.Args[0]).Sprite.SetTexture(e.Args[1] == 1 ? 0 : 2);
break;
case 11:
Console.WriteLine($"E: Player {e.Args[0]} won");
if(Client.Player.state.pid == e.Args[0]) UIManager.ShowVictoryScreen();
break;
}
}
}

View file

@ -0,0 +1,59 @@
using System;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using MonoGameLibrary.Graphics;
namespace FNAF_Clone.GUI;
public class JumpscareUIElement : UIElement {
private int twitchHorizontal;
private int twitchVertical;
private Point positionDefault;
private Random random;
private float defaultScaleMultiplier;
private float twitchScale;
private bool playing = false;
private Stopwatch stopwatch = new();
private int duration;
public JumpscareUIElement(TextureRegion texture, Point positionDefault, int twitchHorizontal, int twitchVertical, float defaultScaleMultiplier, float twitchScale, int durationMs = 2000, Action afterStop = null) : base(texture, positionDefault) {
this.twitchHorizontal = twitchHorizontal;
this.twitchVertical = twitchVertical;
this.positionDefault = positionDefault;
random = new Random();
this.defaultScaleMultiplier = defaultScaleMultiplier;
ScaleMultiplier = defaultScaleMultiplier;
this.twitchScale = twitchScale;
duration = durationMs;
Active = false;
Visible = false;
AfterStop = afterStop;
}
// public JumpscareUIElement(UIElement element) : base(element.GetTextures(), element.Bounds.Item1) {}
public void Play() {
playing = true;
Active = true;
Visible = true;
stopwatch.Start();
}
public override void Update() {
if (!playing) return;
SetPosition(new(positionDefault.X + random.Next(-twitchHorizontal, twitchHorizontal), positionDefault.Y + random.Next(-twitchVertical, twitchVertical)));
if (stopwatch.ElapsedMilliseconds >= duration){
playing = false;
Active = false;
Visible = false;
if (AfterStop != null){
AfterStop();
}
}
// ScaleMultiplier = defaultScaleMultiplier + (float)(random.NextDouble() * twitchScale * new[]{-1, 1}[random.Next(2)]);
}
private Action AfterStop;
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Microsoft.VisualBasic.CompilerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MonoGameLibrary.Input;
@ -10,6 +11,7 @@ public class Screen {
public static Dictionary<string, Screen> Screens = new();
public static Screen CurrentScreen{ get; private set; } = Empty;
public static Screen OverlayScreen{ get; private set; } = Empty;
public static void AddScreens(Screen[] screens) {
foreach (var screen in screens){
@ -22,7 +24,10 @@ public class Screen {
}
public static void SetScreen(string id) {
if (CurrentScreen != null){
if (CurrentScreen.temporary){
Screens.Remove(CurrentScreen.Label);
}
else if (CurrentScreen != null){
CurrentScreen.Active = false;
}
CurrentScreen = Screens[id];
@ -33,13 +38,34 @@ public class Screen {
Screens.Remove(id);
}
public static Screen Empty => new("");
public static void UpdateAll() {
foreach (var screen in Screens.Values){
// if (!screen.Active) continue;
screen.Update();
}
}
public static Screen Empty => new(""){temporary = true};
public static void SetOverlayScreen(string id) {
OverlayScreen = Screens[id];
}
public static void DisableOverlay() {
OverlayScreen = Empty;
}
public static void DrawCurrentAndOverlay(SpriteBatch spriteBatch) {
CurrentScreen.Draw(spriteBatch);
OverlayScreen.Draw(spriteBatch);
}
public string Label;
public string Label{ get; }
private Dictionary<string, UIElement> elements = new();
public bool Active { get; private set; } = false;
private InputListenerHook mouseInputHook = new(true);
private bool temporary = false;
public Screen(string label) {
Label = label;
@ -81,8 +107,9 @@ public class Screen {
}
public void Update() {
foreach (var keyValuePair in elements){
keyValuePair.Value.Update();
foreach (var element in elements.Values){
if (!element.Active) continue;
element.Update();
}
}

View file

@ -0,0 +1,54 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGameLibrary.Graphics;
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 TextUIElement(Point corner1, SpriteFont font, Alignment alignment = Alignment.LEFT) : base(corner1, corner1) {
Font = font;
Align(alignment);
}
public override void Draw(SpriteBatch spriteBatch) {
base.Draw(spriteBatch);
align();
spriteBatch.DrawString(Font, Text, screenSpaceBounds.Item1.ToVector2(), Color, 0, origin, ScaleMultiplier, SpriteEffects.None, 0);
}
public void Align(Alignment alignment) {
switch (alignment){
case Alignment.LEFT:
AlignLeft();
break;
case Alignment.RIGHT:
AlignRight();
break;
case Alignment.CENTER:
AlignCenter();
break;
}
}
private void AlignLeft() {
align = () => origin = Vector2.Zero;
}
private void AlignRight() {
align = () => origin = Font.MeasureString(Text);
}
private void AlignCenter() {
align = () => origin = Font.MeasureString(Text) / 2;
}
private Action align;
public enum Alignment {
LEFT, RIGHT, CENTER
}
}

View file

@ -0,0 +1,30 @@
using System;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace FNAF_Clone.GUI;
public class TimerUIElement : TextUIElement{
private Stopwatch stopwatch = new();
public TimerUIElement(Point corner1, SpriteFont font) : base(corner1, font) {
Text = "00:00.000";
}
public override void Update() {
if (stopwatch.IsRunning){
Text = stopwatch.Elapsed.ToString("mm\\:ss\\.fff");
// Text = stopwatch.ElapsedMilliseconds.ToString();
}
}
public void Start() {
stopwatch.Start();
}
public void Stop() {
stopwatch.Stop();
}
}

View file

@ -10,7 +10,7 @@ namespace FNAF_Clone.GUI;
public class UIElement {
public bool Active { get; set; } = false;
public bool Active { get; set; } = true;
public bool Pressable { get; set; } = false;
public bool Visible { get; set; } = true;
@ -19,9 +19,9 @@ public class UIElement {
private set { field = value; UpdateBounds(); }
} // TODO: Change this to support non-rectangular hitboxes
private (Point, Point) screenSpaceBounds;
private List<TextureRegion> textures = new();
private int currentTextureId = 0;
protected (Point, Point) screenSpaceBounds;
public List<TextureRegion> Textures = new();
protected int currentTextureId = 0;
private float _scaleMultiplier = 1;
public float ScaleMultiplier{
@ -44,12 +44,12 @@ public class UIElement {
}
public UIElement(TextureRegion texture, Point position) {
textures.Add(texture);
Textures.Add(texture);
Bounds = (position, position + new Point(texture.Width, texture.Height));
LoadPixelScaleMultiplier();
}
public UIElement(TextureRegion[] textures, Point position) {
this.textures.AddRange(textures);
this.Textures.AddRange(textures);
Bounds = (position, position + new Point(textures[0].Width, textures[0].Height));
LoadPixelScaleMultiplier();
}
@ -60,9 +60,8 @@ public class UIElement {
LoadPixelScaleMultiplier();
}
public void SetTexture(int textureId) {
if (textureId >= textures.Count){
if (textureId >= Textures.Count){
Console.WriteLine($"WARNING: TEXTURE {textureId} OUT OF BOUNDS");
return;
}
@ -84,15 +83,21 @@ public class UIElement {
public virtual void OnMouseHold() { }
public void Draw(SpriteBatch spriteBatch) {
if (!Visible){
public virtual void Draw(SpriteBatch spriteBatch) {
if (!Visible || !Active){
return;
}
textures[currentTextureId].Draw(spriteBatch, screenSpaceBounds.Item1.ToVector2(), Color.White, 0, Vector2.Zero, pixelScaleMultiplier, SpriteEffects.None, 0);
Textures[currentTextureId].Draw(spriteBatch, screenSpaceBounds.Item1.ToVector2(), Color.White, 0, Vector2.Zero, pixelScaleMultiplier, SpriteEffects.None, 0);
// texture.Draw(spriteBatch, bounds.Item1.ToVector2(), Color.White);
}
public void SetPosition(Point position) {
Bounds = (position, position + new Point(textures[0].Width, textures[0].Height));
Bounds = (position, position + new Point(Textures[0].Width, Textures[0].Height));
}
public UIElement Clone() {
return new UIElement(Textures.ToArray(), Bounds.Item1);
}
public TextureRegion[] GetTextures() => Textures.ToArray();
}

View file

@ -5,8 +5,10 @@ using System.Runtime.CompilerServices;
using FNAF_Clone.Map;
using GlobalClassLib;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGameLibrary;
using MonoGameLibrary.Graphics;
using MonoGameLibrary.Input;
namespace FNAF_Clone.GUI;
@ -15,45 +17,57 @@ public class UIManager {
public static class ScreenTypes {
public const string OFFICE = "office";
public const string CAMERAS = "monitor";
public const string OVERLAY = "overlay";
public const string WIN = "win";
public const string LOSE = "lose";
}
private static Screen officeScreen = new(ScreenTypes.OFFICE);
private static Screen monitorScreen = new(ScreenTypes.CAMERAS);
private static Screen overlayScreen = new(ScreenTypes.OVERLAY);
private static Screen winScreen = new(ScreenTypes.WIN);
private static Screen loseScreen = new(ScreenTypes.LOSE);
private static TextureAtlas testAtlas;
private static TextureAtlas officeAtlas;
private static TextureAtlas monitorAtlas;
public static TextureAtlas enemyAtlas;
public static TextureAtlas OfficeAtlas{ get; private set; }
public static TextureAtlas MonitorAtlas{ get; private set; }
public static TextureAtlas EnemyAtlas{ get; private set; }
public static SpriteFont PixelMonoFont{ get; private set; }
public static int GlobalPixelMultiplier{ get; private set; }
private Dictionary<(int, int), UIElement> doorElements = new();
// private Dictionary<(int, int), UIElement> doorElements = new();
private static Dictionary<int, UIElement> enemyElements = new();
private static TimerUIElement timerElement;
private static InputListenerHook monitorSwitchHook;
public static void InitUI() {
GlobalPixelMultiplier = Core.graphicsDevice.Viewport.Height / 360;
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");
testAtlas = TextureAtlas.FromFile(Core.content, "images/testBlocks-definition.xml"); // TODO: move this to its own method
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]);
Screen.AddScreens([officeScreen, monitorScreen, overlayScreen, winScreen, loseScreen]);
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)));
officeScreen.AddElement("office_right", new UIElement([officeAtlas[5], officeAtlas[2]], new Point(440, 0)));
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)));
officeScreen.AddElement("office_right", new UIElement([OfficeAtlas[5], OfficeAtlas[2]], new Point(440, 0)));
// officeScreen.AddElement("test",
// new UIElement(testAtlas[0], Point.Zero)
// {Pressable = true, OnMousePress = () => Console.WriteLine("Pressed!")}
// );
monitorScreen.AddElement("screen", new UIElement(monitorAtlas[0], Point.Zero));
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)));
monitorScreen.AddElement("screen", new UIElement(MonitorAtlas[0], Point.Zero));
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)));
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++){
@ -70,8 +84,15 @@ 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));
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]);
}
@ -86,21 +107,26 @@ public class UIManager {
int targetId = door.Tiles.tile1.GridPosition.y > door.Tiles.tile2.GridPosition.y ? door.Tiles.tile1.Id : door.Tiles.tile2.Id;
UIElement tile = monitorScreen["room"+targetId];
monitorScreen.AddElement("door"+Math.Max(door.Tiles.tile1.Id, door.Tiles.tile2.Id)+"-"+Math.Min(door.Tiles.tile1.Id, door.Tiles.tile2.Id), new UIElement([monitorAtlas[5], monitorAtlas[6]], tile.Bounds.Item1));
monitorScreen.AddElement("door"+Math.Max(door.Tiles.tile1.Id, door.Tiles.tile2.Id)+"-"+Math.Min(door.Tiles.tile1.Id, door.Tiles.tile2.Id), new UIElement([MonitorAtlas[5], MonitorAtlas[6]], tile.Bounds.Item1));
}
}
monitorScreen.AddElement("p1-office-door-left", new UIElement([monitorAtlas[7], monitorAtlas[8]], new Point(400, 272)));
monitorScreen.AddElement("p1-office-door-centre", new UIElement([monitorAtlas[9], monitorAtlas[10]], new Point(400, 272)));
monitorScreen.AddElement("p1-office-door-right", new UIElement([monitorAtlas[11], monitorAtlas[12]], new Point(400, 272)));
monitorScreen.AddElement("p2-office-door-right", new UIElement([monitorAtlas[13], monitorAtlas[14]], new Point(400, 144)));
monitorScreen.AddElement("p2-office-door-centre", new UIElement([monitorAtlas[15], monitorAtlas[16]], new Point(400, 144)));
monitorScreen.AddElement("p2-office-door-left", new UIElement([monitorAtlas[17], monitorAtlas[18]], new Point(400, 144)));
monitorScreen.AddElement("p1-office-door-left", new UIElement([MonitorAtlas[7], MonitorAtlas[8]], new Point(400, 272)));
monitorScreen.AddElement("p1-office-door-centre", new UIElement([MonitorAtlas[9], MonitorAtlas[10]], new Point(400, 272)));
monitorScreen.AddElement("p1-office-door-right", new UIElement([MonitorAtlas[11], MonitorAtlas[12]], new Point(400, 272)));
monitorScreen.AddElement("p2-office-door-right", new UIElement([MonitorAtlas[13], MonitorAtlas[14]], new Point(400, 144)));
monitorScreen.AddElement("p2-office-door-centre", new UIElement([MonitorAtlas[15], MonitorAtlas[16]], new Point(400, 144)));
monitorScreen.AddElement("p2-office-door-left", new UIElement([MonitorAtlas[17], MonitorAtlas[18]], new Point(400, 144)));
}
public static void AddEnemySprite(int id, UIElement sprite) {
public static void AddEnemySprite(int id, UIElement sprite, UIElement jumpscareSprite = null) {
monitorScreen.AddElement($"enemy{id}", sprite);
if (jumpscareSprite != null){
officeScreen.AddElement($"enemy{id}-jumpscare", jumpscareSprite);
jumpscareSprite.Active = false;
jumpscareSprite.Visible = false;
}
enemyElements.Add(id, sprite);
sprite.Visible = false;
}
@ -174,6 +200,30 @@ public class UIManager {
monitorScreen["eye-opponent"].SetPosition(monitorScreen["room"+id].Bounds.Item1);
}
public static void Jumpscare(ClientEnemy enemy) {
Screen.SetScreen(ScreenTypes.OFFICE);
enemy.JumpscareSprite.Play();
timerElement.Stop();
// UIElement jumpscareElement = enemy.Sprite.Clone();
// jumpscareElement.ScaleMultiplier = 2;
// jumpscareElement.SetPosition(new Point(0, 0));
// officeScreen.AddElement("jumpscare", jumpscareElement);
}
public static void ShowVictoryScreen() {
Screen.SetScreen(ScreenTypes.WIN);
Screen.DisableOverlay();
CommandManager.AllowInput(false);
}
public static void ShowDeathScreen() {
Screen.SetScreen(ScreenTypes.LOSE);
Screen.DisableOverlay();
CommandManager.AllowInput(false);
}
// private static Point GetRoomUIPos((int x, int y) pos) {
// return new Point(336 + (32 * pos.x), 144 + (32 * pos.y));
// }

View file

@ -14,8 +14,6 @@ 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);
CommandManager.InitInputListeners();
@ -29,7 +27,7 @@ public class GameMain() : Core("fnafkooo", 1280, 720, false) {
protected override void LoadContent() {
// spriteBatch = new SpriteBatch(GraphicsDevice);
// font = Content.Load<SpriteFont>("font");
}
protected override void Update(GameTime gameTime) {
@ -40,6 +38,7 @@ public class GameMain() : Core("fnafkooo", 1280, 720, false) {
// TODO: Add your update logic here
InputManager.NextInputCycle();
Screen.UpdateAll();
Client.Update();
base.Update(gameTime);
@ -49,7 +48,9 @@ public class GameMain() : Core("fnafkooo", 1280, 720, false) {
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(samplerState:SamplerState.PointClamp);
Screen.CurrentScreen.Draw(spriteBatch);
Screen.DrawCurrentAndOverlay(spriteBatch);
// Screen.CurrentScreen.Draw(spriteBatch);
// spriteBatch.DrawString(font, "Elapsed time: " + gameTime.TotalGameTime.Milliseconds, new Vector2(10, 10), Color.White);
spriteBatch.End();
base.Draw(gameTime);

View file

@ -1,5 +1,6 @@
using FNAF_Server.Map;
using GlobalClassLib;
using PacketLib;
namespace FNAF_Server.Enemies;
@ -18,9 +19,12 @@ public abstract class Enemy : GlobalEnemy<MapTile, TileConnector> {
public override void Spawn(MapTile location) {
base.Spawn(location);
Spawned = true;
// EnemyManager.AddEnemy(this);
}
public abstract void Reset();
public abstract void Attack(ServerPlayer player);
public virtual void Attack(ServerPlayer player) {
Server.SendUpdateToAll([GameEvent.ENEMY_ATTACK(Id, player.state.pid)]);
GameLogic.DeclareWinner(Server.OtherPlayer(player));
}
}

View file

@ -41,11 +41,6 @@ public class LurkEnemy : Enemy {
Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);
}
public override void Attack(ServerPlayer player) {
throw new NotImplementedException();
}
public override void Update() {
base.Update();
@ -58,7 +53,8 @@ public class LurkEnemy : Enemy {
Console.WriteLine($"Enemy {TypeId} ({Name}) moving to {Location.PositionAsString})");
break;
case Pathfinder.Decision.AttackType:
throw new NotImplementedException();
Attack(decision.attackTarget!);
break;
case Pathfinder.Decision.ResetType:
Reset();
break;

View file

@ -33,11 +33,6 @@ public class NekoEnemy : Enemy {
Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);
}
public override void Attack(ServerPlayer player) {
throw new NotImplementedException();
}
public override void Update() {
base.Update();
@ -50,7 +45,8 @@ public class NekoEnemy : Enemy {
Console.WriteLine($"Enemy {TypeId} ({Name}) moving to {Location.PositionAsString})");
break;
case Pathfinder.Decision.AttackType:
throw new NotImplementedException();
Attack(decision.attackTarget!);
break;
case Pathfinder.Decision.ResetType:
Reset();
break;

View file

@ -30,10 +30,6 @@ public class SpotEnemy : Enemy {
Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);
}
public override void Attack(ServerPlayer player) {
throw new NotImplementedException();
}
public override void Update() {
if (GameLogic.NSecondUpdate){
if(!movementOpportunity.Running)
@ -65,7 +61,7 @@ public class SpotEnemy : Enemy {
Reset();
}
else if (pathId == path.Length - 1){
Attack(Server.P1);
Attack(Server.P2);
}
}
else if (p2WatchCounter > p1WatchCounter){
@ -74,7 +70,7 @@ public class SpotEnemy : Enemy {
Reset();
}
else if (pathId == 0){
Attack(Server.P2);
Attack(Server.P1);
}
}

View file

@ -34,8 +34,8 @@ 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(0)).Spawn(MapManager.Get(12));
EnemyManager.AddEnemy(new NekoEnemy(10)).Spawn(MapManager.Get(2));
EnemyManager.AddEnemy(new LurkEnemy(10)).Spawn(MapManager.Get(12));
EnemyManager.AddEnemy(new NekoEnemy(0)).Spawn(MapManager.Get(2));
EnemyManager.AddEnemy(new SpotEnemy(0)).Spawn(MapManager.Get(12));
secondCycleTimer.Start();
@ -48,4 +48,9 @@ public class GameLogic {
NSecondUpdate = false;
}
public static void DeclareWinner(ServerPlayer player) {
Server.SendUpdateToAll([GameEvent.PLAYER_WIN(player.state.pid)]);
Server.Stop();
Console.WriteLine("Player " + player.state.pid + " won!");
}
}

View file

@ -13,6 +13,7 @@ public class Server {
public static ServerPlayer P1;
public static ServerPlayer P2;
public static readonly Dictionary<int, ServerPlayer> Players = new();
public static ServerPlayer OtherPlayer(ServerPlayer player) => player.state.pid == P1.state.pid ? P2 : P1;
private static EventBasedNetListener listener;
private static NetManager server;

View file

@ -19,6 +19,8 @@ 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 int ID{ get; set; }
public bool Hideable => ID < 0;
public int[] Args{ get; private set; }