Bugfixy, odstranění nepoužívaných tříd CameraSystem a ITargetingEnemy, přidání dedikované třídy ReturnToMenuElement, předělání pathfinding algoritmu z DFS na BFS, přesun správy obtížnosti do abstraktní třídy Enemy, přidání scriptů pro kompilaci na aplikaci nevyžadující dotnet

This commit is contained in:
Perry 2026-03-25 16:37:18 +01:00
parent ceac37920b
commit e5d746d597
24 changed files with 258 additions and 138 deletions

View file

@ -5,6 +5,8 @@
<explicitIncludes> <explicitIncludes>
<Path>FNAF_Server</Path> <Path>FNAF_Server</Path>
<Path>build-linux.sh</Path> <Path>build-linux.sh</Path>
<Path>build-self-contained-linux.sh</Path>
<Path>build-self-contained-win.sh</Path>
<Path>launch-server.sh</Path> <Path>launch-server.sh</Path>
</explicitIncludes> </explicitIncludes>
<explicitExcludes /> <explicitExcludes />

View file

@ -1,18 +0,0 @@
namespace ONDClient;
public class CameraSystem {
public bool Enabled { get; private set; }
public int CurrentCamera { get; private set; }
public void FlipUp() {
Enabled = true;
}
public void FlipDown() {
Enabled = false;
}
public void SetCamera(int camera) {
}
}

View file

@ -20,13 +20,13 @@ public class Client {
CONNECTED, CONNECTED,
ACCEPTED, ACCEPTED,
GAME_STARTING, GAME_STARTING,
DISCONNECTED,
GAME_IN_PROGRESS, GAME_IN_PROGRESS,
SERVER_NOT_FOUND,
SERVER_FULL,
ERROR ERROR
} }
public static string StatusText{ get; private set; }
public static ConnectionState State { get; private set; } = ConnectionState.IDLE; public static ConnectionState State { get; set; } = ConnectionState.IDLE;
private static EventBasedNetListener listener = new(); private static EventBasedNetListener listener = new();
private static NetManager client; private static NetManager client;
@ -68,6 +68,13 @@ public class Client {
State = ConnectionState.CONNECTED; State = ConnectionState.CONNECTED;
SendPacket(new JoinPacket {username = Player.username == "" ? "Anonymous" : Player.username}, DeliveryMethod.ReliableOrdered); SendPacket(new JoinPacket {username = Player.username == "" ? "Anonymous" : Player.username}, DeliveryMethod.ReliableOrdered);
}; };
listener.PeerDisconnectedEvent += (peer, info) => {
State = ConnectionState.IDLE;
Console.WriteLine("Disconnected from Server:\n" + info.Reason);
State = ConnectionState.DISCONNECTED;
StatusText = info.Reason.ToString();
};
} }
public static void Connect(string endPoint, int port) { public static void Connect(string endPoint, int port) {

View file

@ -114,6 +114,7 @@ public class EventProcessor {
UIManager.DisplayGameUI(); UIManager.DisplayGameUI();
UIManager.StartTimer(); UIManager.StartTimer();
SoundManager.StartAmbience(); SoundManager.StartAmbience();
Client.State = Client.ConnectionState.GAME_IN_PROGRESS;
break; break;
case 13: case 13:

View file

@ -1,14 +1,15 @@
using System;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
namespace ONDClient.GUI; namespace ONDClient.GUI;
public class LoadingUIElement : TextUIElement { public class LoadingUIElement : TextUIElement {
private string expectedEndpoint; private Func<string> expectedEndpointGetter;
private Client.ConnectionState lastState = Client.ConnectionState.IDLE; 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) { public LoadingUIElement(Point corner1, SpriteFont font, Func<string> expectedEndpointGetter, Alignment alignment = Alignment.CENTER, bool autoBounds = true) : base(corner1, font, alignment, autoBounds) {
this.expectedEndpoint = expectedEndpoint; this.expectedEndpointGetter = expectedEndpointGetter;
Active = true; Active = true;
// Color = Color.LightGray; // Color = Color.LightGray;
} }
@ -19,10 +20,10 @@ public class LoadingUIElement : TextUIElement {
switch (Client.State){ switch (Client.State){
case Client.ConnectionState.CONNECTING: case Client.ConnectionState.CONNECTING:
Text = "Connecting to " + expectedEndpoint; Text = "Connecting to " + expectedEndpointGetter();
break; break;
case Client.ConnectionState.CONNECTED: case Client.ConnectionState.CONNECTED:
Text = "Connected to " + expectedEndpoint; Text = "Connected to " + expectedEndpointGetter();
break; break;
case Client.ConnectionState.ACCEPTED: case Client.ConnectionState.ACCEPTED:
Text = "Waiting for opponent..."; Text = "Waiting for opponent...";
@ -32,6 +33,12 @@ public class LoadingUIElement : TextUIElement {
Color = Color.White; Color = Color.White;
// ScaleMultiplier = 1.5f; // ScaleMultiplier = 1.5f;
break; break;
case Client.ConnectionState.IDLE:
Text = "Idle";
break;
case Client.ConnectionState.DISCONNECTED:
Text = "Disconnected: " + Client.StatusText;
break;
} }
} }
} }

View file

@ -0,0 +1,40 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MonoGameLibrary.Input;
namespace ONDClient.GUI;
public class ReturnToMenuElement : TextUIElement{
private bool listening = false;
private InputListenerHook inputHook;
public ReturnToMenuElement(Point corner1, SpriteFont font, Alignment alignment = Alignment.CENTER) : base(corner1, font, alignment, false) {
Visible = false;
Text = "Press Space to return to main menu";
inputHook = new InputListenerHook(false);
InputManager.AddListener("return-to-menu",Keys.Space, () => {
UIManager.DisplayMainMenu();
listening = false;
Visible = false;
Client.Disconnect();
},
InputTiming.PRESS, inputHook);
}
public override void Update() {
if ((Client.State == Client.ConnectionState.IDLE || Client.State == Client.ConnectionState.DISCONNECTED || Client.State == Client.ConnectionState.ACCEPTED) &&
(Screen.CurrentScreen.Label == UIManager.ScreenTypes.LOADING || Screen.CurrentScreen.Label == UIManager.ScreenTypes.LOSE || Screen.CurrentScreen.Label == UIManager.ScreenTypes.WIN)){
if (!listening){
listening = true;
Visible = true;
inputHook.Enabled = true;
}
}
else {
Visible = false;
listening = false;
inputHook.Enabled = false;
}
}
}

View file

@ -27,13 +27,16 @@ public class TextUIElement : UIElement {
Font = font; Font = font;
AutoBounds = autoBounds; AutoBounds = autoBounds;
Align(alignment); Align(alignment);
Visible = true;
} }
public TextUIElement(Point corner1, Point corner2, SpriteFont font, Alignment alignment = Alignment.LEFT) : base(corner1, corner2) { public TextUIElement(Point corner1, Point corner2, SpriteFont font, Alignment alignment = Alignment.LEFT) : base(corner1, corner2) {
Font = font; Font = font;
Align(alignment); Align(alignment);
Visible = true;
} }
public override void Draw(SpriteBatch spriteBatch) { public override void Draw(SpriteBatch spriteBatch) {
if(!Visible || !Active) return;
base.Draw(spriteBatch); base.Draw(spriteBatch);
align(); align();
spriteBatch.DrawString(Font, Text, screenSpaceBounds.Item1.ToVector2(), Color, 0, origin, pixelScaleMultiplier * UNIVERSAL_TEXT_SCALE_MULTIPLIER, SpriteEffects.None, 0); spriteBatch.DrawString(Font, Text, screenSpaceBounds.Item1.ToVector2(), Color, 0, origin, pixelScaleMultiplier * UNIVERSAL_TEXT_SCALE_MULTIPLIER, SpriteEffects.None, 0);

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Mime;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using MonoGameLibrary; using MonoGameLibrary;
@ -89,9 +90,10 @@ public class UIElement {
public virtual void OnMouseHold() { } public virtual void OnMouseHold() { }
public virtual void Draw(SpriteBatch spriteBatch) { public virtual void Draw(SpriteBatch spriteBatch) {
if (!Visible || !Active){ if (!Visible || !Active || Textures.Count == 0){
return; 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); // texture.Draw(spriteBatch, bounds.Item1.ToVector2(), Color.White);
} }

View file

@ -108,8 +108,12 @@ public class UIManager {
monitorScreen.AddElement("power-p2", new PowerIndicator(new(powerLabel.Bounds.Item1.X + 10, powerLabel.Bounds.Item2.Y + 10), PixelMonoFont, Client.Opponent, ""){Color = new Color(220, 10, 10, 255)}); monitorScreen.AddElement("power-p2", new PowerIndicator(new(powerLabel.Bounds.Item1.X + 10, powerLabel.Bounds.Item2.Y + 10), PixelMonoFont, Client.Opponent, ""){Color = new Color(220, 10, 10, 255)});
monitorScreen.AddElement("power-p1", new PowerIndicator( new (powerP1.Bounds.Item1.X, powerP1.Bounds.Item2.Y + 5), PixelMonoFont, Client.Player, ""){Color = new Color(15, 190, 247, 255)}); monitorScreen.AddElement("power-p1", new PowerIndicator( new (powerP1.Bounds.Item1.X, powerP1.Bounds.Item2.Y + 5), PixelMonoFont, Client.Player, ""){Color = new Color(15, 190, 247, 255)});
ReturnToMenuElement returnToMenuElement = new ReturnToMenuElement(new(320, 290), PixelMonoFont);
winScreen.AddElement("win-text", new TextUIElement(new(320, 180), PixelMonoFont, TextUIElement.Alignment.CENTER){Text = "YOU WIN", Color = Color.Green}); 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}); loseScreen.AddElement("lose-text", new TextUIElement(new(320, 180), PixelMonoFont, TextUIElement.Alignment.CENTER){Text = "YOU LOSE", Color = Color.Red});
winScreen.AddElement("press-space", returnToMenuElement);
loseScreen.AddElement("press-space", returnToMenuElement);
loadingScreen.AddElement("press-space", returnToMenuElement);
MenuInputField usernameField = (MenuInputField)menuScreen.AddElement("username-field", new MenuInputField(PixelMonoFont, new(20, 20), "USERNAME: ")); 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")); 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"));
@ -118,9 +122,7 @@ public class UIManager {
Text = "CONNECT", Text = "CONNECT",
Pressable = true, Pressable = true,
OnMousePress = () => { 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.Player.username = usernameField.Text;
Client.Connect(field.Text, 9012); Client.Connect(field.Text, 9012);
Screen.SetScreen(ScreenTypes.LOADING); Screen.SetScreen(ScreenTypes.LOADING);
@ -136,7 +138,7 @@ public class UIManager {
}}); }});
loadingScreen.AddElement("loading-text", new LoadingUIElement(new(320, 180), PixelMonoFont, field.Text)); loadingScreen.AddElement("loading-text", new LoadingUIElement(new(320, 180), PixelMonoFont, () => field.Text));
} }
@ -319,7 +321,8 @@ public class UIManager {
Screen.DisableOverlay(); Screen.DisableOverlay();
CommandManager.AllowGameControls(false); CommandManager.AllowGameControls(false);
SoundManager.StopAmbience(); SoundManager.StopAmbience();
InputManager.AddListener(Keys.Space, DisplayMainMenu, InputTiming.PRESS, new InputListenerHook(true, true)); SoundManager.StopNekoPurr();
// InputManager.AddListener(Keys.Space, DisplayMainMenu, InputTiming.PRESS, new InputListenerHook(true, true));
} }
public static void ShowDeathScreen() { public static void ShowDeathScreen() {
@ -327,8 +330,8 @@ public class UIManager {
Screen.DisableOverlay(); Screen.DisableOverlay();
CommandManager.AllowGameControls(false); CommandManager.AllowGameControls(false);
SoundManager.StopAmbience(); SoundManager.StopAmbience();
SoundManager.StopNekoPurr();
InputManager.AddListener(Keys.Space, DisplayMainMenu, InputTiming.PRESS, new InputListenerHook(true, true)); // InputManager.AddListener(Keys.Space, DisplayMainMenu, InputTiming.PRESS, new InputListenerHook(true, true));
} }
public static void ResetUI() { public static void ResetUI() {
@ -345,4 +348,8 @@ public class UIManager {
// return new Point(336 + (32 * pos.x), 144 + (32 * pos.y)); // return new Point(336 + (32 * pos.x), 144 + (32 * pos.y));
// } // }
public static void SetLoadingScreenMessage(string text) {
}
} }

View file

@ -10,7 +10,7 @@ using ONDClient.GUI;
namespace ONDClient; namespace ONDClient;
public class GameMain() : Core("fnafkooo", 1280, 720, false) { public class GameMain() : Core("OND", 1280, 720, false) {
// private GraphicsDeviceManager _graphics; // private GraphicsDeviceManager _graphics;
// private SpriteBatch _spriteBatch; // private SpriteBatch _spriteBatch;

View file

@ -6,7 +6,7 @@ using PacketLib;
namespace ONDServer.Enemies; namespace ONDServer.Enemies;
public class DashEnemy : Enemy { public class DashEnemy : Enemy {
public DashEnemy(int difficulty) : base(difficulty) { public DashEnemy(int difficulty) : base(difficulty, 5000) {
movementOpportunity = new(5000); movementOpportunity = new(5000);
SetDifficulty(difficulty); SetDifficulty(difficulty);
} }
@ -15,7 +15,7 @@ public class DashEnemy : Enemy {
public override int TypeId{ get; } = (int)EnemyType.DASH; public override int TypeId{ get; } = (int)EnemyType.DASH;
public override bool BlocksTile{ get; set; } = false; public override bool BlocksTile{ get; set; } = false;
private MovementOpportunity movementOpportunity; // private MovementOpportunity movementOpportunity;
private readonly (MapTile tile, Direction p1AttackDir, Direction p2AttackDir)[] positions =[ private readonly (MapTile tile, Direction p1AttackDir, Direction p2AttackDir)[] positions =[
(MapManager.Get(7), Direction.EAST, Direction.WEST), (MapManager.Get(7), Direction.EAST, Direction.WEST),
@ -34,10 +34,10 @@ public class DashEnemy : Enemy {
Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]); Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);
} }
public override void SetDifficulty(int difficulty) { // public override void SetDifficulty(int difficulty) {
Difficulty = difficulty; // Difficulty = difficulty;
movementOpportunity.MovementChance = (2 * Math.Sign(difficulty) + difficulty) / 12.0; // movementOpportunity.MovementChance = (2 * Math.Sign(difficulty) + difficulty) / 12.0;
} // }
public override void Spawn(MapTile location) { public override void Spawn(MapTile location) {
currentPosIndex = positions.ToList().FindIndex(p => p.tile == location); currentPosIndex = positions.ToList().FindIndex(p => p.tile == location);

View file

@ -7,8 +7,12 @@ namespace ONDServer.Enemies;
public abstract class Enemy : GlobalEnemy<MapTile, TileConnector> { public abstract class Enemy : GlobalEnemy<MapTile, TileConnector> {
public int Difficulty { get; protected set; } public int Difficulty { get; protected set; }
protected Enemy(int difficulty) { protected MovementOpportunity movementOpportunity;
Difficulty = difficulty;
protected Enemy(int difficulty, int movementInterval) {
movementOpportunity = new(movementInterval);
SetDifficulty(difficulty);
} }
public abstract bool BlocksTile { get; set; } public abstract bool BlocksTile { get; set; }
@ -31,5 +35,8 @@ public abstract class Enemy : GlobalEnemy<MapTile, TileConnector> {
GameLogic.DeclareWinner(Server.OtherPlayer(player)); GameLogic.DeclareWinner(Server.OtherPlayer(player));
} }
public abstract void SetDifficulty(int difficulty); public virtual void SetDifficulty(int difficulty) {
Difficulty = difficulty;
movementOpportunity.MovementChance = ((5 + Math.Pow(1.5f, Difficulty)) * Math.Sign(Difficulty)) / (5 + Math.Pow(1.5f, 10));
}
} }

View file

@ -1,5 +0,0 @@
namespace ONDServer.Enemies;
public interface ITargetingEnemy {
ServerPlayer Target { get; set; }
}

View file

@ -18,15 +18,13 @@ public class LurkEnemy : Enemy {
// private static readonly double CHANCE_DENOMINATOR = 2 + Math.Pow(1.5f, 10); // d10 Lurk will have a 100% chance to move // private static readonly double CHANCE_DENOMINATOR = 2 + Math.Pow(1.5f, 10); // d10 Lurk will have a 100% chance to move
// private double movementChance => (2 + Math.Pow(1.5f, Difficulty)) / CHANCE_DENOMINATOR; // chance scales exponentially using a constant multiplier // private double movementChance => (2 + Math.Pow(1.5f, Difficulty)) / CHANCE_DENOMINATOR; // chance scales exponentially using a constant multiplier
private MovementOpportunity movementOpportunity;
public ServerPlayer? Target{ get; set; } public ServerPlayer? Target{ get; set; }
public LurkEnemy(int difficulty) : base(difficulty) { public LurkEnemy(int difficulty) : base(difficulty, 5000) {
pathfinder = new LurkPathfinder(this, 1); pathfinder = new LurkPathfinder(this, 1);
movementOpportunity = new MovementOpportunity(5000); // movementOpportunity = new MovementOpportunity(5000);
Target = null; Target = null;
SetDifficulty(difficulty); // SetDifficulty(difficulty);
} }
public override void Spawn(MapTile location) { public override void Spawn(MapTile location) {
base.Spawn(location); base.Spawn(location);
@ -37,20 +35,20 @@ public class LurkEnemy : Enemy {
} }
public override void Reset() { public override void Reset() {
pathfinder.TakenPath.Clear(); Target.state.power -= 80;
pathfinder.TakenPath.Add(Location);
Target.state.power -= 30;
MapTile[] resetLocations = new[]{MapManager.Get(7), MapManager.Get(12), MapManager.Get(17)}.Where(t => EnemyManager.GetByLocation(t).Length == 0).ToArray(); MapTile[] resetLocations = new[]{MapManager.Get(7), MapManager.Get(12), MapManager.Get(17)}.Where(t => EnemyManager.GetByLocation(t).Length == 0).ToArray();
Location = resetLocations[new Random().Next(resetLocations.Length)]; Location = resetLocations[new Random().Next(resetLocations.Length)];
pathfinder.TakenPath.Clear();
pathfinder.TakenPath.Add(Location);
Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]); Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);
} }
public override void SetDifficulty(int difficulty) { // public override void SetDifficulty(int difficulty) {
Difficulty = difficulty; // Difficulty = difficulty;
movementOpportunity.MovementChance = ((5 + Math.Pow(1.5f, Difficulty)) * Math.Sign(Difficulty)) / (5 + Math.Pow(1.5f, 10)); // movementOpportunity.MovementChance = ((5 + Math.Pow(1.5f, Difficulty)) * Math.Sign(Difficulty)) / (5 + Math.Pow(1.5f, 10));
} // }
public override void Update() { public override void Update() {
base.Update(); base.Update();

View file

@ -11,23 +11,14 @@ public class MareEnemy : Enemy {
public override bool BlocksTile{ get; set; } = true; public override bool BlocksTile{ get; set; } = true;
private MarePathfinder pathfinder; private MarePathfinder pathfinder;
private MovementOpportunity movementOpportunity; // private MovementOpportunity movementOpportunity;
public ServerPlayer Target{ get; set; } public ServerPlayer? Target{ get; set; }
public MareEnemy(int difficulty) : base(difficulty) { public MareEnemy(int difficulty) : base(difficulty, 6000) {
pathfinder = new MarePathfinder(this, 1); pathfinder = new MarePathfinder(this, 1);
if (Server.P1.state.power > Server.P2.state.power){
Target = Server.P2;
}
else if(Server.P1.state.power < Server.P2.state.power){
Target = Server.P1;
}
else{
Target = new Random().Next(2) == 0 ? Server.P1 : Server.P2;
}
movementOpportunity = new MovementOpportunity(5000); // movementOpportunity = new MovementOpportunity(5000);
SetDifficulty(difficulty); // SetDifficulty(difficulty);
} }
public override void Reset() { public override void Reset() {
@ -38,19 +29,28 @@ public class MareEnemy : Enemy {
Location = decision.nextTile!; Location = decision.nextTile!;
} }
Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);; Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);
} }
public override void SetDifficulty(int difficulty) { // public override void SetDifficulty(int difficulty) {
Difficulty = difficulty; // Difficulty = difficulty;
movementOpportunity.MovementChance = // movementOpportunity.MovementChance =
((5 + Math.Pow(1.5f, Difficulty)) * Math.Sign(Difficulty)) / (5 + Math.Pow(1.5f, 10)); // ((5 + Math.Pow(1.5f, Difficulty)) * Math.Sign(Difficulty)) / (5 + Math.Pow(1.5f, 10));
} // }
public override void Spawn(MapTile location) { public override void Spawn(MapTile location) {
base.Spawn(location); base.Spawn(location);
// stopwatch.Start(); // stopwatch.Start()
if (Server.P1.state.power > Server.P2.state.power){
Target = Server.P2;
}
else if(Server.P1.state.power < Server.P2.state.power){
Target = Server.P1;
}
else{
Target = new Random().Next(2) == 0 ? Server.P1 : Server.P2;
}
movementOpportunity.Start(); movementOpportunity.Start();
pathfinder.TakenPath.Add(Location); pathfinder.TakenPath.Add(Location);
@ -65,6 +65,8 @@ public class MareEnemy : Enemy {
Attack(Location.Owner); Attack(Location.Owner);
} }
if (Target == null) return;
Pathfinder.Decision decision = pathfinder.DecideNext(MapManager.Get(Target.state.officeTileId)); Pathfinder.Decision decision = pathfinder.DecideNext(MapManager.Get(Target.state.officeTileId));
switch (decision.type){ switch (decision.type){
case Pathfinder.Decision.MoveType: case Pathfinder.Decision.MoveType:

View file

@ -5,13 +5,13 @@ using PacketLib;
namespace ONDServer.Enemies; namespace ONDServer.Enemies;
public class NekoEnemy : Enemy { public class NekoEnemy : Enemy {
private MovementOpportunity movementOpportunity; // private MovementOpportunity movementOpportunity;
private RoamingPathfinder pathfinder; private RoamingPathfinder pathfinder;
public NekoEnemy(int difficulty) : base(difficulty) { public NekoEnemy(int difficulty) : base(difficulty, 4000) {
pathfinder = new RoamingPathfinder(this, 1){AdditionalTileFilter = t => Aggressive || t.Owner == null || !t.Lit}; pathfinder = new RoamingPathfinder(this, 1){AdditionalTileFilter = t => Aggressive || t.Owner == null || !t.Lit};
movementOpportunity = new MovementOpportunity(5000); // movementOpportunity = new MovementOpportunity(5000);
SetDifficulty(difficulty); // SetDifficulty(difficulty);
} }
@ -33,19 +33,20 @@ public class NekoEnemy : Enemy {
} }
public override void Reset() { public override void Reset() {
pathfinder.TakenPath.Clear();
MapTile[] resetLocations = new[]{MapManager.Get(7), MapManager.Get(12), MapManager.Get(17)}.Where(t => EnemyManager.GetByLocation(t).Length == 0).ToArray(); MapTile[] resetLocations = new[]{MapManager.Get(7), MapManager.Get(12), MapManager.Get(17)}.Where(t => EnemyManager.GetByLocation(t).Length == 0).ToArray();
Location = resetLocations[new Random().Next(resetLocations.Length)]; Location = resetLocations[new Random().Next(resetLocations.Length)];
pathfinder.TakenPath.Clear();
pathfinder.TakenPath.Add(Location);
Aggressive = false; Aggressive = false;
Target = Server.OtherPlayer(Target); Target = Server.OtherPlayer(Target);
Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]); Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);
} }
public override void SetDifficulty(int difficulty) { // public override void SetDifficulty(int difficulty) {
Difficulty = difficulty; // Difficulty = difficulty;
movementOpportunity.MovementChance = // movementOpportunity.MovementChance =
((5 + Math.Pow(1.5f, Difficulty)) * Math.Sign(Difficulty)) / (5 + Math.Pow(1.5f, 10)); // ((5 + Math.Pow(1.5f, Difficulty)) * Math.Sign(Difficulty)) / (5 + Math.Pow(1.5f, 10));
} // }
public override void Update() { public override void Update() {
base.Update(); base.Update();
@ -70,7 +71,8 @@ public class NekoEnemy : Enemy {
} }
} }
if (Target != null && (movementOpportunity.CheckAndRoll() || (Aggressive && GameLogic.NSecondUpdate))){ bool succeed = movementOpportunity.CheckAndRoll();
if (Target != null && (succeed || (Aggressive && GameLogic.NSecondUpdate))){
Pathfinder.Decision decision = pathfinder.DecideNext(MapManager.Get(Target.state.officeTileId)); Pathfinder.Decision decision = pathfinder.DecideNext(MapManager.Get(Target.state.officeTileId));
switch (decision.type){ switch (decision.type){
case Pathfinder.Decision.MoveType: case Pathfinder.Decision.MoveType:

View file

@ -12,38 +12,62 @@ public class RoamingPathfinder : Pathfinder {
public List<MapTile> TakenPath { get; } = new(); public List<MapTile> TakenPath { get; } = new();
private Random random = new(); private Random random = new();
// private Queue<MapTile> tilesToCrawl = new();
// private Dictionary<MapTile, int> distances = new();
// private Func<TileConnector, MapTile, bool> defaultConnectorFilter; // private Func<TileConnector, MapTile, bool> defaultConnectorFilter;
public RoamingPathfinder(Enemy enemy, int tolerance) : base(enemy) { public RoamingPathfinder(Enemy enemy, int tolerance) : base(enemy) {
this.tolerance = tolerance; this.tolerance = tolerance;
} }
protected Dictionary<MapTile, int> Crawl(MapTile tile) { protected Dictionary<MapTile, int> Search(MapTile startingTile) {
Dictionary<MapTile, int> distances = new(){ [tile] = 0 }; Dictionary<MapTile, int> distances = new(){ [startingTile] = 0 };
CrawlStep(tile, distances); Queue<MapTile> tilesToSearch = new();
tilesToSearch.Enqueue(startingTile);
while (tilesToSearch.Count > 0){
MapTile currentTile = tilesToSearch.Dequeue();
List<MapTile> neighbours = GetNeighbours(currentTile,
c =>
AdditionalConnectorFilter(c) &&
(!c.Blocked || c.Type == ConnectorType.DOOR_OFFICE) &&
(!distances.ContainsKey(c.OtherTile(currentTile)) || distances[c.OtherTile(currentTile)] > distances[currentTile] + c.Value),
t =>
AdditionalTileFilter(t) &&
(!Enemy.BlocksTile || EnemyManager.GetByLocation(t).All(e => !e.BlocksTile || e == Enemy)) &&
Server.Players.All(p => p.Value.state.officeTileId != t.Id));
neighbours.ForEach(t => {
distances[t] = distances[currentTile] + currentTile.GetConnector(t)!.Value;
tilesToSearch.Enqueue(t);
});
}
return distances; return distances;
} }
private void CrawlStep(MapTile tile, Dictionary<MapTile, int> distances) { // private void CrawlStep(MapTile tile) {
List<MapTile> neighbours = GetNeighbours(tile, // List<MapTile> neighbours = GetNeighbours(tile,
c => // c =>
AdditionalConnectorFilter(c) && // AdditionalConnectorFilter(c) &&
(!c.Blocked || c.Type == ConnectorType.DOOR_OFFICE) && // (!c.Blocked || c.Type == ConnectorType.DOOR_OFFICE) &&
tile != Enemy.Location && // tile != Enemy.Location &&
(!distances.ContainsKey(c.OtherTile(tile)) || distances[c.OtherTile(tile)] > distances[tile] + c.Value), // (!distances.ContainsKey(c.OtherTile(tile)) || distances[c.OtherTile(tile)] > distances[tile] + c.Value),
t => // t =>
AdditionalTileFilter(t) && // AdditionalTileFilter(t) &&
(!Enemy.BlocksTile || EnemyManager.GetByLocation(t).All(e => !e.BlocksTile || e == Enemy)) && // (!Enemy.BlocksTile || EnemyManager.GetByLocation(t).All(e => !e.BlocksTile || e == Enemy)) &&
Server.Players.All(p => p.Value.state.officeTileId != t.Id)); // Server.Players.All(p => p.Value.state.officeTileId != t.Id));
//
neighbours.ForEach(t => distances[t] = distances[tile] + tile.GetConnector(t)!.Value); // neighbours.ForEach(t => distances[t] = distances[tile] + tile.GetConnector(t)!.Value);
//
if (neighbours.Count != 0){ // if (neighbours.Count != 0){
neighbours.ForEach(t => CrawlStep(t, distances)); // neighbours.ForEach(t => CrawlStep(t));
} // }
} // }
public override Decision DecideNext(MapTile goal) { public override Decision DecideNext(MapTile goal) {
Dictionary<MapTile, int> distances = Crawl(goal); Dictionary<MapTile, int> distances = Search(goal);
List<MapTile> closerTiles = GetNeighbours(Enemy.Location, List<MapTile> closerTiles = GetNeighbours(Enemy.Location,
c => AdditionalConnectorFilter(c) && !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE, c => AdditionalConnectorFilter(c) && !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,

View file

@ -5,11 +5,11 @@ using PacketLib;
namespace ONDServer.Enemies; namespace ONDServer.Enemies;
public class SpotEnemy : Enemy { public class SpotEnemy : Enemy {
public SpotEnemy(int difficulty) : base(difficulty) { public SpotEnemy(int difficulty) : base(difficulty, 6000) {
path = [MapManager.Get(10), MapManager.Get(11), MapManager.Get(12), MapManager.Get(13), MapManager.Get(14)]; path = [MapManager.Get(10), MapManager.Get(11), MapManager.Get(12), MapManager.Get(13), MapManager.Get(14)];
pathId = 2; pathId = 2;
movementOpportunity = new(6000); // movementOpportunity = new(6000);
SetDifficulty(difficulty); // SetDifficulty(difficulty);
} }
public override string Name{ get; } = "Spot"; public override string Name{ get; } = "Spot";
@ -18,7 +18,7 @@ public class SpotEnemy : Enemy {
public bool Active{ get; set; } = false; public bool Active{ get; set; } = false;
private MovementOpportunity movementOpportunity; // private MovementOpportunity movementOpportunity;
private MapTile[] path; private MapTile[] path;
private int pathId; private int pathId;
@ -26,15 +26,18 @@ public class SpotEnemy : Enemy {
private int p2WatchCounter = 0; private int p2WatchCounter = 0;
public override void Reset() { public override void Reset() {
if (Location.Owner != null){
Location.Owner.state.power -= 200;
}
pathId = 2; pathId = 2;
Location = path[pathId]; Location = path[pathId];
Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]); Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);
} }
public override void SetDifficulty(int difficulty) { // public override void SetDifficulty(int difficulty) {
Difficulty = difficulty; // Difficulty = difficulty;
movementOpportunity.MovementChance = (2 * Math.Sign(difficulty) + difficulty) / 12.0; // movementOpportunity.MovementChance = (2 * Math.Sign(difficulty) + difficulty) / 12.0;
} // }
public override void Update() { public override void Update() {
if (GameLogic.NSecondUpdate){ if (GameLogic.NSecondUpdate){
@ -52,7 +55,7 @@ public class SpotEnemy : Enemy {
if (movementOpportunity.CheckAndRoll()){ if (movementOpportunity.CheckAndRoll()){
if(!Active) { if(!Active) {
Active = true; Active = true;
movementOpportunity.Interval = 12_000; movementOpportunity.Interval = 10_000;
movementOpportunity.GuaranteeSuccess(true); movementOpportunity.GuaranteeSuccess(true);
Server.SendUpdateToAll([GameEvent.SPOT_SET_ACTIVE(Id, true)]); Server.SendUpdateToAll([GameEvent.SPOT_SET_ACTIVE(Id, true)]);
} }

View file

@ -30,6 +30,7 @@ public class GameLogic {
private static Random random = new(); private static Random random = new();
private static bool unlimitedPower = false; // Debug private static bool unlimitedPower = false; // Debug
private static bool noPhases = true; // Debug
// public const int POWER_MAX = 1000; // public const int POWER_MAX = 1000;
// public static int P1Power{ get; set; } = Power.MAX_POWER_VALUE; // public static int P1Power{ get; set; } = Power.MAX_POWER_VALUE;
@ -71,6 +72,7 @@ public class GameLogic {
Server.SendPacket(new MapInitPacket{Connectors = connectorsConverted, UpsideDown = true, YourTiles = p2Tiles.ToArray(), OpponentTiles = p1Tiles.ToArray(), LitTiles = neutralTiles.ToArray()}, Server.P2.peer); Server.SendPacket(new MapInitPacket{Connectors = connectorsConverted, UpsideDown = true, YourTiles = p2Tiles.ToArray(), OpponentTiles = p1Tiles.ToArray(), LitTiles = neutralTiles.ToArray()}, Server.P2.peer);
spawnOrder = [[new SpotEnemy(3), new MareEnemy(3)], [new DashEnemy(6)], [new LurkEnemy(3), new NekoEnemy(3)]]; spawnOrder = [[new SpotEnemy(3), new MareEnemy(3)], [new DashEnemy(6)], [new LurkEnemy(3), new NekoEnemy(3)]];
// Enemy test = new SpotEnemy(3);
Thread.Sleep(3000); Thread.Sleep(3000);
secondCycleTimer.Start(); secondCycleTimer.Start();
@ -79,12 +81,12 @@ public class GameLogic {
// EnemyManager.AddEnemy(new SpotEnemy(10)).Spawn(MapManager.Get(12)); // EnemyManager.AddEnemy(new SpotEnemy(10)).Spawn(MapManager.Get(12));
// EnemyManager.AddEnemy(new LurkEnemy(10)).Spawn(MapManager.Get(12)); // EnemyManager.AddEnemy(new LurkEnemy(10)).Spawn(MapManager.Get(12));
// EnemyManager.AddEnemy(new NekoEnemy(10)).Spawn(MapManager.Get(2)); EnemyManager.AddEnemy(new NekoEnemy(7)).Spawn(MapManager.Get(2));
// EnemyManager.AddEnemy(new DashEnemy(10)).Spawn(MapManager.Get(12)); // EnemyManager.AddEnemy(new DashEnemy(10)).Spawn(MapManager.Get(12));
// EnemyManager.AddEnemy(new MareEnemy(10)).Spawn(MapManager.Get(22)); // EnemyManager.AddEnemy(new MareEnemy(10)).Spawn(MapManager.Get(22));
EnemyManager.AddEnemy(new LurkEnemy(4)).Spawn(MapManager.Get(2)); // EnemyManager.AddEnemy(new LurkEnemy(4)).Spawn(MapManager.Get(2));
EnemyManager.AddEnemy(new NekoEnemy(4)).Spawn(MapManager.Get(22)); // EnemyManager.AddEnemy(new NekoEnemy(4)).Spawn(MapManager.Get(22));
} }
public static void Update() { public static void Update() {
@ -162,6 +164,8 @@ public class GameLogic {
} }
private static void NextPhase() { private static void NextPhase() {
if(noPhases) return;
PhaseCounter++; PhaseCounter++;
Server.SendUpdateToAll([GameEvent.NEXT_PHASE()]); Server.SendUpdateToAll([GameEvent.NEXT_PHASE()]);

View file

@ -140,10 +140,13 @@ public class Server {
} }
public static void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { public static void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) {
GameLogic.DeclareWinner(OtherPlayer(Players.Values.First(p => Equals(p.peer, peer)))); if (Players.Count == 2){
GameLogic.DeclareWinner(OtherPlayer(Players.Values.First(p => Equals(p.peer, peer))));
}
if (peer.Tag != null) { Players.Remove(peer.Id);
Players.Remove(peer.Id); if (Players.Count == 0){
Stop();
} }
} }

View file

@ -16,7 +16,9 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASpriteBatch_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa69b121f85054691b2a8d28643003c23136600_003F98_003F17ad8de4_003FSpriteBatch_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASpriteBatch_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa69b121f85054691b2a8d28643003c23136600_003F98_003F17ad8de4_003FSpriteBatch_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_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_003AThrowHelpers_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5e56f40aa42e4fc4b94786ec57da7544d1a400_003F48_003F95b0ef9d_003FThrowHelpers_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelpers_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5e56f40aa42e4fc4b94786ec57da7544d1a400_003F48_003F95b0ef9d_003FThrowHelpers_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_003F34c341e0ffea43388ce71ea7b328acb678a00_003Fc6_003Fc045192f_003FThrowHelper_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_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_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ffd792102e2a04b889c29784ca9de4981d1a000_003F8f_003F6419ac7c_003FThrowHelper_002Ecs_002Fz_003A2_002D1/@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_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> <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>
<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_003Ffd792102e2a04b889c29784ca9de4981d1a000_003F2a_003Fa31b08a3_003FThread_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_003Ffd792102e2a04b889c29784ca9de4981d1a000_003F2a_003Fa31b08a3_003FThread_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

15
build-self-contained-linux.sh Executable file
View file

@ -0,0 +1,15 @@
target_dir=bin/OneNightDuel-linux-x64
launch_server_script="while :; do bin/ONDServer; done"
dotnet publish MonoGameLibrary
ln -sfrv MonoGameLibrary/MonoGameLibrary/bin/Release/net9.0/publish/MonoGameLibrary.dll ONDClient/link/MonoGameLibrary.dll
dotnet publish --runtime linux-x64 --self-contained
mkdir -p bin/OneNightDuel-linux-x64/bin/
cp -r bin/Client/net9.0/linux-x64/publish/* $target_dir/bin/
cp -r bin/Server/net9.0/linux-x64/publish/* $target_dir/bin/
ln -sfrv $target_dir/bin/ONDClient $target_dir/OneNightDuel
echo "$launch_server_script" > $target_dir/launch-server-standalone.sh
chmod +x $target_dir/launch-server-standalone.sh

14
build-self-contained-win.sh Executable file
View file

@ -0,0 +1,14 @@
target_dir=bin/OneNightDuel-win-x64
launch_server_script="while :; do bin\ONDServer.exe; done"
launch_client_script="bin\ONDClient.exe"
dotnet publish MonoGameLibrary
ln -sfrv MonoGameLibrary/MonoGameLibrary/bin/Release/net9.0/publish/MonoGameLibrary.dll ONDClient/link/MonoGameLibrary.dll
dotnet publish --runtime win-x64 --self-contained
mkdir -p bin/OneNightDuel-win-x64/bin/
cp -r bin/Client/net9.0/win-x64/publish/* $target_dir/bin/
cp -r bin/Server/net9.0/win-x64/publish/* $target_dir/bin/
echo "$launch_server_script" > $target_dir/launch-server-standalone.bat
echo "$launch_client_script" > $target_dir/OneNightDuel.bat