Zvuky, změny v pathfindingu, přidány ventilace

This commit is contained in:
Perry 2026-03-19 20:10:45 +01:00
parent 4fdff0a0cc
commit c5adebb2db
36 changed files with 527 additions and 143 deletions

View file

@ -5,6 +5,7 @@
<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_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_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_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_003AEventManifestOptions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ffd792102e2a04b889c29784ca9de4981d1a000_003Fe5_003F92feb77a_003FEventManifestOptions_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_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_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_003AInputManager_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4e0fe18725844db38e9480edfd0e34983e00_003F41_003Fc6f0d8ab_003FInputManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInputManager_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4e0fe18725844db38e9480edfd0e34983e00_003F41_003Fc6f0d8ab_003FInputManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

View file

@ -34,6 +34,7 @@ public class CommandManager {
Client.Player.state.monitorUp = !Client.Player.state.monitorUp; Client.Player.state.monitorUp = !Client.Player.state.monitorUp;
UIManager.ChangeMonitorState(Client.Player.state.monitorUp); UIManager.ChangeMonitorState(Client.Player.state.monitorUp);
Client.SendCommands([PlayerCommand.SET_MONITOR(Client.Player.state.monitorUp)]); Client.SendCommands([PlayerCommand.SET_MONITOR(Client.Player.state.monitorUp)]);
SoundManager.PlayMonitorFlip();
} }
private static void ToggleDoorLeft() => SendToggleDoor(Direction.EAST); private static void ToggleDoorLeft() => SendToggleDoor(Direction.EAST);
@ -54,6 +55,7 @@ public class CommandManager {
if (direction == Direction.SOUTH) return; if (direction == Direction.SOUTH) return;
Client.Player.state.doorStates[(int)direction] = !Client.Player.state.doorStates[(int)direction]; Client.Player.state.doorStates[(int)direction] = !Client.Player.state.doorStates[(int)direction];
UIManager.ChangeDoorState(direction, Client.Player.state.doorStates[(int)direction]); UIManager.ChangeDoorState(direction, Client.Player.state.doorStates[(int)direction]);
SoundManager.PlayDoor(Client.Player.state.doorStates[(int)direction]);
Client.SendCommands([PlayerCommand.SET_DOOR_OFFICE(direction, Client.Player.state.doorStates[(int)direction])]); Client.SendCommands([PlayerCommand.SET_DOOR_OFFICE(direction, Client.Player.state.doorStates[(int)direction])]);
} }
@ -63,6 +65,7 @@ public class CommandManager {
connector.Blocked = !connector.Blocked; connector.Blocked = !connector.Blocked;
Client.SendCommands([PlayerCommand.SET_DOOR_REMOTE(connector.Id, connector.Blocked)]); Client.SendCommands([PlayerCommand.SET_DOOR_REMOTE(connector.Id, connector.Blocked)]);
SoundManager.PlayDoorRemote(connector.Blocked);
UIManager.ChangeRemoteDoorState(connector.Id, connector.Blocked); UIManager.ChangeRemoteDoorState(connector.Id, connector.Blocked);
// Console.WriteLine("Changed door state to: " + (connector.Blocked ? "blocked" : "open") + " for door " + connector); // Console.WriteLine("Changed door state to: " + (connector.Blocked ? "blocked" : "open") + " for door " + connector);
// add UIManager toggle door // add UIManager toggle door
@ -73,6 +76,7 @@ public class CommandManager {
Client.Player.state.camera = id; Client.Player.state.camera = id;
UIManager.ChangeCamera(id); UIManager.ChangeCamera(id);
SoundManager.PlayCameraSwitch();
Client.SendCommands([PlayerCommand.SWITCH_CAM(id)]); Client.SendCommands([PlayerCommand.SWITCH_CAM(id)]);
MapTileProjection tile = ClientMapManager.Get(id); MapTileProjection tile = ClientMapManager.Get(id);
// add UIManager switch camera // add UIManager switch camera
@ -88,6 +92,7 @@ public class CommandManager {
if(!Client.Player.state.monitorUp || ClientMapManager.Get(id).Owner != Client.Player) return; if(!Client.Player.state.monitorUp || ClientMapManager.Get(id).Owner != Client.Player) return;
ClientMapManager.Get(id).Lit = state; ClientMapManager.Get(id).Lit = state;
UIManager.UpdateCameras([id]); UIManager.UpdateCameras([id]);
SoundManager.PlayLight(state);
Client.SendCommands([PlayerCommand.SET_LIGHT(id, state)]); Client.SendCommands([PlayerCommand.SET_LIGHT(id, state)]);
} }
} }

View file

@ -95,3 +95,123 @@
/processorParam:TextureFormat=Compressed /processorParam:TextureFormat=Compressed
/build:ponderosa.spritefont /build:ponderosa.spritefont
#begin sounds/ambience.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/ambience.wav
#begin sounds/camera-switch.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/camera-switch.wav
#begin sounds/dash-move.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/dash-move.wav
#begin sounds/dash-sounds-raw.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/dash-sounds-raw.wav
#begin sounds/door-close-remote.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/door-close-remote.wav
#begin sounds/door-close.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/door-close.wav
#begin sounds/door-open-remote.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/door-open-remote.wav
#begin sounds/door-open.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/door-open.wav
#begin sounds/jumpscare.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/jumpscare.wav
#begin sounds/light-off.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/light-off.wav
#begin sounds/light-on.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/light-on.wav
#begin sounds/mare-move.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/mare-move.wav
#begin sounds/monitor-flip.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/monitor-flip.wav
#begin sounds/neko-angry.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/neko-angry.wav
#begin sounds/neko-move1.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/neko-move1.wav
#begin sounds/neko-purr.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/neko-purr.wav
#begin sounds/powerout.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/powerout.wav
#begin sounds/spot-activate.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/spot-activate.wav
#begin sounds/spot-move.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/spot-move.wav
#begin sounds/vent-walk.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:sounds/vent-walk.wav

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -77,25 +77,29 @@ public class EventProcessor {
int oldPos = ClientEnemyManager.Get(e.Args[0]).Location!.Id; int oldPos = ClientEnemyManager.Get(e.Args[0]).Location!.Id;
ClientEnemyManager.Move(e.Args[0], ClientMapManager.Get(e.Args[1])); ClientEnemyManager.Move(e.Args[0], ClientMapManager.Get(e.Args[1]));
UIManager.UpdateCameras([oldPos, e.Args[1]]); UIManager.UpdateCameras([oldPos, e.Args[1]]);
SoundManager.PlayEnemyMove(ClientEnemyManager.Get(e.Args[0]));
break; break;
case 8: // attack case 8: // attack
Console.WriteLine($"E: Enemy {e.Args[0]} attacked player {e.Args[1]}"); // TODO: add an arg to indicate lethality 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) { if (e.Args[1] == Client.Player.state.pid) {
UIManager.Jumpscare(ClientEnemyManager.Get(e.Args[0])); UIManager.Jumpscare(ClientEnemyManager.Get(e.Args[0]));
SoundManager.PlayJumpscare();
} }
break; break;
case 9: case 9: // reset
Console.WriteLine($"E: Enemy {e.Args[0]} reset to {e.Args[1]}"); Console.WriteLine($"E: Enemy {e.Args[0]} reset to {e.Args[1]}");
int preResetPos = ClientEnemyManager.Get(e.Args[0]).Location!.Id; int preResetPos = ClientEnemyManager.Get(e.Args[0]).Location!.Id;
ClientEnemyManager.Move(e.Args[0], ClientMapManager.Get(e.Args[1])); ClientEnemyManager.Move(e.Args[0], ClientMapManager.Get(e.Args[1]));
UIManager.UpdateCameras([preResetPos, e.Args[1]]); UIManager.UpdateCameras([preResetPos, e.Args[1]]);
SoundManager.PlayEnemyReset(ClientEnemyManager.Get(e.Args[0]));
break; break;
case 10: case 10:
Console.WriteLine($"E: Spot:{e.Args[0]} turned {(e.Args[1] == 1 ? "on" : " off")}"); 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 : 1); ClientEnemyManager.Get(e.Args[0]).Sprite.SetTexture(e.Args[1] == 1 ? 0 : 1);
SoundManager.PlaySpotActivate();
break; break;
case 11: case 11:
@ -103,10 +107,11 @@ public class EventProcessor {
if(Client.Player.state.pid == e.Args[0]) UIManager.ShowVictoryScreen(); if(Client.Player.state.pid == e.Args[0]) UIManager.ShowVictoryScreen();
break; break;
case 12: case 12: // game start
Console.WriteLine("E: Game started"); Console.WriteLine("E: Game started");
UIManager.DisplayGameUI(); UIManager.DisplayGameUI();
UIManager.StartTimer(); UIManager.StartTimer();
SoundManager.StartAmbience();
break; break;
case 13: case 13:
@ -140,6 +145,7 @@ public class EventProcessor {
UIManager.ChangeDoorState(Direction.WEST, false); UIManager.ChangeDoorState(Direction.WEST, false);
CommandManager.AllowGameControls(false); CommandManager.AllowGameControls(false);
UIManager.ChangeMonitorState(false); UIManager.ChangeMonitorState(false);
SoundManager.PlayPowerOut();
} }
else{ else{
UIManager.ChangeDoorStateOpponent(Direction.EAST, false); UIManager.ChangeDoorStateOpponent(Direction.EAST, false);
@ -159,6 +165,15 @@ public class EventProcessor {
} }
ClientMapManager.Get(e.Args[1]).Lit = lightState; ClientMapManager.Get(e.Args[1]).Lit = lightState;
UIManager.UpdateCameras([e.Args[1]]);
break;
case 16: // neko anger
SoundManager.PlayNekoAnger();
break;
case 17:
SoundManager.PlayVentWalk();
break; break;
} }

View file

@ -49,6 +49,8 @@ public class UIManager {
private static InputListenerHook monitorSwitchHook; private static InputListenerHook monitorSwitchHook;
private static bool fullBright = false; // Debug
public static void LoadAssets() { public static void LoadAssets() {
testAtlas = TextureAtlas.FromFile(Core.content, "images/testBlocks-definition.xml"); testAtlas = TextureAtlas.FromFile(Core.content, "images/testBlocks-definition.xml");
OfficeAtlas = TextureAtlas.FromFile(Core.content, "images/office-definition.xml"); OfficeAtlas = TextureAtlas.FromFile(Core.content, "images/office-definition.xml");
@ -239,6 +241,7 @@ public class UIManager {
public static void ChangeMonitorState(bool state) { public static void ChangeMonitorState(bool state) {
Screen.SetScreen(state ? ScreenTypes.CAMERAS : ScreenTypes.OFFICE); Screen.SetScreen(state ? ScreenTypes.CAMERAS : ScreenTypes.OFFICE);
UpdateCameras([Client.Player.state.camera]);
} }
public static void ChangeMonitorStateOpponent(bool state) { public static void ChangeMonitorStateOpponent(bool state) {
@ -259,7 +262,7 @@ public class UIManager {
} }
if (camIds.Contains(Client.Player.state.camera)){ if (camIds.Contains(Client.Player.state.camera)){
bool lit = ClientMapManager.Get(Client.Player.state.camera).Lit; bool lit = ClientMapManager.Get(Client.Player.state.camera).Lit || fullBright;
cameraView.Visible = lit; cameraView.Visible = lit;
enemyElements.Values.Where(e => e.Visible).ToList().ForEach(e => e.Visible = false); enemyElements.Values.Where(e => e.Visible).ToList().ForEach(e => e.Visible = false);
ClientEnemy[] enemies = ClientEnemyManager.GetByLocation(ClientMapManager.Get(Client.Player.state.camera)); ClientEnemy[] enemies = ClientEnemyManager.GetByLocation(ClientMapManager.Get(Client.Player.state.camera));
@ -270,6 +273,13 @@ public class UIManager {
enemyElement.Visible = true; enemyElement.Visible = true;
enemyElement.SetTexture(lit); enemyElement.SetTexture(lit);
} }
if (!lit && Client.Player.state.monitorUp && enemies.Any(e => e.TypeId == (int)EnemyType.NEKO)){
SoundManager.StartNekoPurr();
}
else{
SoundManager.StopNekoPurr();
}
} }
} }
@ -293,6 +303,7 @@ public class UIManager {
Screen.SetScreen(ScreenTypes.WIN); Screen.SetScreen(ScreenTypes.WIN);
Screen.DisableOverlay(); Screen.DisableOverlay();
CommandManager.AllowGameControls(false); CommandManager.AllowGameControls(false);
SoundManager.StopAmbience();
InputManager.AddListener(Keys.Space, DisplayMainMenu, InputTiming.PRESS, new InputListenerHook(true, true)); InputManager.AddListener(Keys.Space, DisplayMainMenu, InputTiming.PRESS, new InputListenerHook(true, true));
} }
@ -300,6 +311,7 @@ public class UIManager {
Screen.SetScreen(ScreenTypes.LOSE); Screen.SetScreen(ScreenTypes.LOSE);
Screen.DisableOverlay(); Screen.DisableOverlay();
CommandManager.AllowGameControls(false); CommandManager.AllowGameControls(false);
SoundManager.StopAmbience();
InputManager.AddListener(Keys.Space, DisplayMainMenu, InputTiming.PRESS, new InputListenerHook(true, true)); InputManager.AddListener(Keys.Space, DisplayMainMenu, InputTiming.PRESS, new InputListenerHook(true, true));
} }

View file

@ -29,6 +29,7 @@ public class GameMain() : Core("fnafkooo", 1280, 720, false) {
protected override void LoadContent() { protected override void LoadContent() {
UIManager.LoadAssets(); UIManager.LoadAssets();
SoundManager.LoadSounds();
// spriteBatch = new SpriteBatch(GraphicsDevice); // spriteBatch = new SpriteBatch(GraphicsDevice);
// font = Content.Load<SpriteFont>("font"); // font = Content.Load<SpriteFont>("font");
} }

164
FNAF_Clone/SoundManager.cs Normal file
View file

@ -0,0 +1,164 @@
using System;
using GlobalClassLib;
using Microsoft.Xna.Framework.Audio;
using MonoGameLibrary;
namespace FNAF_Clone;
public static class SoundManager {
private static SoundEffect ambience;
private static SoundEffect ventWalk;
private static SoundEffect camSwitch;
private static SoundEffect lightOff;
private static SoundEffect lightOn;
private static SoundEffect doorClose;
private static SoundEffect doorCloseRemote;
private static SoundEffect doorOpen;
private static SoundEffect doorOpenRemote;
private static SoundEffect monitorFlip;
private static SoundEffect powerOut;
private static SoundEffect mareMove;
private static SoundEffect dashMove;
private static SoundEffect spotMove;
private static SoundEffect spotActivate;
private static SoundEffect nekoPurr;
private static SoundEffect jumpscare;
private static SoundEffect nekoAnger;
private static SoundEffect nekoMove;
private static SoundEffectInstance ambienceInstance;
private static SoundEffectInstance nekoPurrInstance;
private static Random random = new();
public static void LoadSounds() {
ambience = Core.content.Load<SoundEffect>("sounds/ambience");
ventWalk = Core.content.Load<SoundEffect>("sounds/vent-walk");
camSwitch = Core.content.Load<SoundEffect>("sounds/camera-switch");
lightOff = Core.content.Load<SoundEffect>("sounds/light-off");
lightOn = Core.content.Load<SoundEffect>("sounds/light-on");
doorClose = Core.content.Load<SoundEffect>("sounds/door-close");
doorCloseRemote = Core.content.Load<SoundEffect>("sounds/door-close-remote");
doorOpen = Core.content.Load<SoundEffect>("sounds/door-open");
doorOpenRemote = Core.content.Load<SoundEffect>("sounds/door-open-remote");
monitorFlip = Core.content.Load<SoundEffect>("sounds/monitor-flip");
powerOut = Core.content.Load<SoundEffect>("sounds/powerout");
mareMove = Core.content.Load<SoundEffect>("sounds/mare-move");
dashMove = Core.content.Load<SoundEffect>("sounds/dash-move");
spotMove = Core.content.Load<SoundEffect>("sounds/spot-move");
spotActivate = Core.content.Load<SoundEffect>("sounds/spot-activate");
nekoPurr = Core.content.Load<SoundEffect>("sounds/neko-purr");
jumpscare = Core.content.Load<SoundEffect>("sounds/jumpscare");
nekoAnger = Core.content.Load<SoundEffect>("sounds/neko-angry");
nekoMove = Core.content.Load<SoundEffect>("sounds/neko-move1");
nekoPurrInstance = nekoPurr.CreateInstance();
ambienceInstance = ambience.CreateInstance();
}
public static void StartAmbience() {
ambienceInstance = ambience.CreateInstance();
ambienceInstance.IsLooped = true;
ambienceInstance.Volume = 0.75f;
ambienceInstance.Play();
}
public static void StopAmbience() {
ambienceInstance.Stop();
}
public static void PlayDoor(bool doorState) {
if (doorState){
doorClose.Play();
}
else{
doorOpen.Play();
}
}
public static void PlayDoorRemote(bool doorState) {
if (doorState){
doorCloseRemote.Play();
}
else{
doorOpenRemote.Play();
}
}
public static void PlayLight(bool lightState) {
if (lightState){
lightOn.Play();
}
else{
lightOff.Play();
}
}
public static void PlayJumpscare() => jumpscare.Play();
public static void PlayPowerOut() => powerOut.Play();
public static void PlayNekoMove() => nekoMove.Play();
public static void PlayNekoAnger() => nekoAnger.Play();
public static void PlaySpotActivate() => spotActivate.Play();
public static void PlaySpotMove() => spotMove.Play();
public static void PlayDashMove() => dashMove.Play();
public static void PlayMareMove() => mareMove.Play();
public static void PlayMonitorFlip() => monitorFlip.Play();
public static void PlayCameraSwitch() => camSwitch.Play();
public static void PlayVentWalk() => ventWalk.Play();
public static void StartNekoPurr() {
nekoPurrInstance = nekoPurr.CreateInstance();
nekoPurrInstance.IsLooped = true;
nekoPurrInstance.Play();
}
public static void StopNekoPurr() {
nekoPurrInstance.Stop();
}
private static SoundEffectInstance GetRandomisedPitchInstance(SoundEffect effect) {
SoundEffectInstance instance = effect.CreateInstance();
instance.Pitch = (float)(random.NextDouble() - 0.5) / 5;
instance.Play();
return instance;
}
public static void PlayEnemyMove(ClientEnemy enemy) {
switch ((EnemyType)enemy.TypeId){
case EnemyType.NEKO:
PlayNekoMove();
break;
case EnemyType.SPOT:
PlaySpotMove();
break;
case EnemyType.MARE:
PlayMareMove();
break;
}
}
public static void PlayEnemyReset(ClientEnemy enemy) {
switch ((EnemyType)enemy.TypeId){
case EnemyType.NEKO:
PlayNekoMove();
break;
case EnemyType.SPOT:
PlaySpotMove();
break;
case EnemyType.MARE:
PlayMareMove();
break;
case EnemyType.DASH:
PlayDashMove();
break;
}
}
}

View file

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

View file

@ -32,13 +32,13 @@ public class LurkEnemy : Enemy {
base.Spawn(location); base.Spawn(location);
// stopwatch.Start(); // stopwatch.Start();
movementOpportunity.Start(); movementOpportunity.Start();
pathfinder.takenPath.Add(Location); pathfinder.TakenPath.Add(Location);
Server.SendUpdateToAll([GameEvent.ENEMY_SPAWN(TypeId, Id, Difficulty, Location.Id)]); Server.SendUpdateToAll([GameEvent.ENEMY_SPAWN(TypeId, Id, Difficulty, Location.Id)]);
} }
public override void Reset() { public override void Reset() {
pathfinder.takenPath.Clear(); pathfinder.TakenPath.Clear();
pathfinder.takenPath.Add(Location); pathfinder.TakenPath.Add(Location);
Target.state.power -= 30; Target.state.power -= 30;
@ -69,6 +69,9 @@ public class LurkEnemy : Enemy {
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:
if (Location.GetConnector(decision.nextTile!)!.Type == ConnectorType.VENT){
Server.SendUpdateToAll([GameEvent.VENT_USED()]);
}
Location = decision.nextTile!; Location = decision.nextTile!;
Server.SendUpdateToAll([GameEvent.ENEMY_MOVEMENT(Id, Location.Id)]); Server.SendUpdateToAll([GameEvent.ENEMY_MOVEMENT(Id, Location.Id)]);
Console.WriteLine($"Enemy {TypeId} ({Name}) moving to {Location.PositionAsString})"); Console.WriteLine($"Enemy {TypeId} ({Name}) moving to {Location.PositionAsString})");
@ -86,46 +89,43 @@ public class LurkEnemy : Enemy {
} }
private class LurkPathfinder : RoamingPathfinder { private class LurkPathfinder : RoamingPathfinder {
private Random random = new(); // private Random random = new();
private int tolerance; public LurkPathfinder(Enemy enemy, int tolerance) : base(enemy, tolerance) {
public List<MapTile> takenPath = new();
public LurkPathfinder(Enemy enemy, int tolerance) : base(enemy) {
this.tolerance = tolerance; this.tolerance = tolerance;
} }
public override Decision DecideNext(MapTile goal) { // public override Decision DecideNext(MapTile goal) {
Dictionary<MapTile, int> distances = Crawl(goal); // Dictionary<MapTile, int> distances = Crawl(goal);
//
List<MapTile> closerTiles = GetNeighbours(Enemy.Location, // List<MapTile> closerTiles = GetNeighbours(Enemy.Location,
c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE, // c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,
t => distances.ContainsKey(t) && distances[t] <= distances[Enemy.Location] + tolerance); // t => distances.ContainsKey(t) && distances[t] <= distances[Enemy.Location] + tolerance);
if (closerTiles.Contains(goal)){ // if (closerTiles.Contains(goal)){
if (Enemy.Location.GetConnector(goal)!.Blocked){ // if (Enemy.Location.GetConnector(goal)!.Blocked){
return Decision.Reset(); // return Decision.Reset();
} // }
//
return Decision.Attack(((LurkEnemy)Enemy).Target); // return Decision.Attack(((LurkEnemy)Enemy).Target);
} // }
//
if (closerTiles.Count != 0 && closerTiles.All(t => takenPath.Contains(t))){ // if (closerTiles.Count != 0 && closerTiles.All(t => takenPath.Contains(t))){
takenPath.Clear(); // takenPath.Clear();
return DecideNext(goal); // return DecideNext(goal);
} // }
closerTiles.RemoveAll(t => takenPath.Contains(t)); // closerTiles.RemoveAll(t => takenPath.Contains(t));
//
closerTiles.ForEach(t => distances[t] += Enemy.Location.GetConnector(t)!.Value); // closerTiles.ForEach(t => distances[t] += Enemy.Location.GetConnector(t)!.Value);
double roll = random.NextDouble() * closerTiles.Sum(t => 1.0 / distances[t]); // double roll = random.NextDouble() * closerTiles.Sum(t => 1.0 / distances[t]);
foreach (var tile in closerTiles){ // foreach (var tile in closerTiles){
if (roll <= 1.0 / distances[tile]){ // if (roll <= 1.0 / distances[tile]){
takenPath.Add(tile); // takenPath.Add(tile);
return Decision.Move(tile); // return Decision.Move(tile);
} // }
roll -= 1.0 / distances[tile]; // roll -= 1.0 / distances[tile];
} // }
//
return Decision.Wait(); // return Decision.Wait();
} // }
} }
} }

View file

@ -31,7 +31,7 @@ public class MareEnemy : Enemy {
} }
public override void Reset() { public override void Reset() {
pathfinder.takenPath.Clear(); pathfinder.TakenPath.Clear();
Target = Server.OtherPlayer(Target); Target = Server.OtherPlayer(Target);
Pathfinder.Decision decision = pathfinder.DecideNext(MapManager.Get(Target.state.officeTileId)); Pathfinder.Decision decision = pathfinder.DecideNext(MapManager.Get(Target.state.officeTileId));
if (decision.type == Pathfinder.Decision.MoveType){ if (decision.type == Pathfinder.Decision.MoveType){
@ -53,7 +53,7 @@ public class MareEnemy : Enemy {
// stopwatch.Start(); // stopwatch.Start();
movementOpportunity.Start(); movementOpportunity.Start();
pathfinder.takenPath.Add(Location); pathfinder.TakenPath.Add(Location);
Server.SendUpdateToAll([GameEvent.ENEMY_SPAWN(TypeId, Id, Difficulty, Location.Id)]); Server.SendUpdateToAll([GameEvent.ENEMY_SPAWN(TypeId, Id, Difficulty, Location.Id)]);
} }
@ -87,46 +87,44 @@ public class MareEnemy : Enemy {
private class MarePathfinder : RoamingPathfinder { private class MarePathfinder : RoamingPathfinder {
private Random random = new(); // private Random random = new();
private int tolerance; public MarePathfinder(Enemy enemy, int tolerance) : base(enemy, tolerance) {
public List<MapTile> takenPath = new();
public MarePathfinder(Enemy enemy, int tolerance) : base(enemy) {
this.tolerance = tolerance; this.tolerance = tolerance;
AdditionalConnectorFilter = c => c.Type != ConnectorType.VENT;
} }
public override Decision DecideNext(MapTile goal) { // public override Decision DecideNext(MapTile goal) {
Dictionary<MapTile, int> distances = Crawl(goal); // Dictionary<MapTile, int> distances = Crawl(goal);
//
List<MapTile> closerTiles = GetNeighbours(Enemy.Location, // List<MapTile> closerTiles = GetNeighbours(Enemy.Location,
c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE, // c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,
t => distances.ContainsKey(t) && distances[t] <= distances[Enemy.Location] + tolerance); // t => distances.ContainsKey(t) && distances[t] <= distances[Enemy.Location] + tolerance);
if (closerTiles.Contains(goal)){ // if (closerTiles.Contains(goal)){
if (Enemy.Location.GetConnector(goal)!.Blocked){ // if (Enemy.Location.GetConnector(goal)!.Blocked){
return Decision.Reset(); // return Decision.Reset();
} // }
//
return Decision.Attack(((MareEnemy)Enemy).Target); // return Decision.Attack(((MareEnemy)Enemy).Target);
} // }
//
if (closerTiles.Count != 0 && closerTiles.All(t => takenPath.Contains(t))){ // if (closerTiles.Count != 0 && closerTiles.All(t => takenPath.Contains(t))){
takenPath.Clear(); // takenPath.Clear();
return DecideNext(goal); // return DecideNext(goal);
} // }
closerTiles.RemoveAll(t => takenPath.Contains(t)); // closerTiles.RemoveAll(t => takenPath.Contains(t));
//
closerTiles.ForEach(t => distances[t] += Enemy.Location.GetConnector(t)!.Value); // closerTiles.ForEach(t => distances[t] += Enemy.Location.GetConnector(t)!.Value);
double roll = random.NextDouble() * closerTiles.Sum(t => 1.0 / distances[t]); // double roll = random.NextDouble() * closerTiles.Sum(t => 1.0 / distances[t]);
foreach (var tile in closerTiles){ // foreach (var tile in closerTiles){
if (roll <= 1.0 / distances[tile]){ // if (roll <= 1.0 / distances[tile]){
takenPath.Add(tile); // takenPath.Add(tile);
return Decision.Move(tile); // return Decision.Move(tile);
} // }
roll -= 1.0 / distances[tile]; // roll -= 1.0 / distances[tile];
} // }
//
return Decision.Wait(); // return Decision.Wait();
} // }
} }
} }

View file

@ -6,10 +6,10 @@ namespace FNAF_Server.Enemies;
public class NekoEnemy : Enemy { public class NekoEnemy : Enemy {
private MovementOpportunity movementOpportunity; private MovementOpportunity movementOpportunity;
private NekoPathfinder pathfinder; private RoamingPathfinder pathfinder;
public NekoEnemy(int difficulty) : base(difficulty) { public NekoEnemy(int difficulty) : base(difficulty) {
pathfinder = new NekoPathfinder(this, 1); 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);
@ -28,15 +28,16 @@ public class NekoEnemy : Enemy {
base.Spawn(location); base.Spawn(location);
// stopwatch.Start(); // stopwatch.Start();
movementOpportunity.Start(); movementOpportunity.Start();
pathfinder.takenPath.Add(Location); pathfinder.TakenPath.Add(Location);
Server.SendUpdateToAll([GameEvent.ENEMY_SPAWN(TypeId, Id, Difficulty, Location.Id)]); Server.SendUpdateToAll([GameEvent.ENEMY_SPAWN(TypeId, Id, Difficulty, Location.Id)]);
} }
public override void Reset() { public override void Reset() {
pathfinder.takenPath.Clear(); 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)];
Aggressive = false; Aggressive = false;
Target = Server.OtherPlayer(Target);
Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]); Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);
} }
@ -49,7 +50,7 @@ public class NekoEnemy : Enemy {
public override void Update() { public override void Update() {
base.Update(); base.Update();
if (Location.Owner != null && Location.Lit){ if (Location.Owner != null && Location.Lit && !Aggressive){
Aggressive = true; Aggressive = true;
Server.SendUpdateToAll([GameEvent.NEKO_ANGERED(Location.Owner.state.pid, Id)]); Server.SendUpdateToAll([GameEvent.NEKO_ANGERED(Location.Owner.state.pid, Id)]);
} }
@ -70,6 +71,9 @@ public class NekoEnemy : Enemy {
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:
if (Location.GetConnector(decision.nextTile!)!.Type == ConnectorType.VENT){
Server.SendUpdateToAll([GameEvent.VENT_USED()]);
}
Location = decision.nextTile!; Location = decision.nextTile!;
Server.SendUpdateToAll([GameEvent.ENEMY_MOVEMENT(Id, Location.Id)]); Server.SendUpdateToAll([GameEvent.ENEMY_MOVEMENT(Id, Location.Id)]);
Console.WriteLine($"Enemy {TypeId} ({Name}) moving to {Location.PositionAsString})"); Console.WriteLine($"Enemy {TypeId} ({Name}) moving to {Location.PositionAsString})");
@ -88,49 +92,46 @@ public class NekoEnemy : Enemy {
} }
private class NekoPathfinder : RoamingPathfinder { private class NekoPathfinder : RoamingPathfinder {
private Random random = new(); // private Random random = new();
private int tolerance; public NekoPathfinder(Enemy enemy, int tolerance) : base(enemy, tolerance) {
public List<MapTile> takenPath = new();
public NekoPathfinder(Enemy enemy, int tolerance) : base(enemy) {
this.tolerance = tolerance; this.tolerance = tolerance;
AdditionalTileFilter = t => ((NekoEnemy)Enemy).Aggressive || t.Owner == null || !t.Lit; AdditionalTileFilter = t => ((NekoEnemy)Enemy).Aggressive || t.Owner == null || !t.Lit;
} }
public override Decision DecideNext(MapTile goal) { // public override Decision DecideNext(MapTile goal) {
Dictionary<MapTile, int> distances = Crawl(goal); // Dictionary<MapTile, int> distances = Crawl(goal);
//
List<MapTile> closerTiles = GetNeighbours(Enemy.Location, // List<MapTile> closerTiles = GetNeighbours(Enemy.Location,
c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE, // c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,
t => // t =>
distances.ContainsKey(t) && // distances.ContainsKey(t) &&
distances[t] <= distances[Enemy.Location] + tolerance); // distances[t] <= distances[Enemy.Location] + tolerance);
if (closerTiles.Contains(goal)){ // if (closerTiles.Contains(goal)){
if (Enemy.Location.GetConnector(goal)!.Blocked){ // if (Enemy.Location.GetConnector(goal)!.Blocked){
return Decision.Reset(); // return Decision.Reset();
} // }
//
return Decision.Attack(((NekoEnemy)Enemy).Target); // return Decision.Attack(((NekoEnemy)Enemy).Target);
} // }
//
if (closerTiles.Count != 0 && closerTiles.All(t => takenPath.Contains(t))){ // if (closerTiles.Count != 0 && closerTiles.All(t => takenPath.Contains(t))){
takenPath.Clear(); // takenPath.Clear();
return DecideNext(goal); // return DecideNext(goal);
} // }
closerTiles.RemoveAll(t => takenPath.Contains(t)); // closerTiles.RemoveAll(t => takenPath.Contains(t));
//
closerTiles.ForEach(t => distances[t] += Enemy.Location.GetConnector(t)!.Value); // closerTiles.ForEach(t => distances[t] += Enemy.Location.GetConnector(t)!.Value);
double roll = random.NextDouble() * closerTiles.Sum(t => 1.0 / distances[t]); // double roll = random.NextDouble() * closerTiles.Sum(t => 1.0 / distances[t]);
foreach (var tile in closerTiles){ // foreach (var tile in closerTiles){
if (roll <= 1.0 / distances[tile]){ // if (roll <= 1.0 / distances[tile]){
takenPath.Add(tile); // takenPath.Add(tile);
return Decision.Move(tile); // return Decision.Move(tile);
} // }
roll -= 1.0 / distances[tile]; // roll -= 1.0 / distances[tile];
} // }
//
return Decision.Wait(); // return Decision.Wait();
} // }
} }
} }

View file

@ -3,13 +3,18 @@ using GlobalClassLib;
namespace FNAF_Server.Enemies; namespace FNAF_Server.Enemies;
public abstract class RoamingPathfinder : Pathfinder { public class RoamingPathfinder : Pathfinder {
protected Predicate<MapTile> AdditionalTileFilter = _ => true; public Predicate<MapTile> AdditionalTileFilter{ get; set; } = _ => true;
protected Predicate<TileConnector> AdditionalConnectorFilter = _ => true; public Predicate<TileConnector> AdditionalConnectorFilter{ get; set; } = _ => true;
protected int tolerance;
public List<MapTile> TakenPath { get; } = new();
private Random random = new();
// private Func<TileConnector, MapTile, bool> defaultConnectorFilter; // private Func<TileConnector, MapTile, bool> defaultConnectorFilter;
protected RoamingPathfinder(Enemy enemy) : base(enemy) { public RoamingPathfinder(Enemy enemy, int tolerance) : base(enemy) {
this.tolerance = tolerance;
} }
protected Dictionary<MapTile, int> Crawl(MapTile tile) { protected Dictionary<MapTile, int> Crawl(MapTile tile) {
@ -23,10 +28,10 @@ public abstract class RoamingPathfinder : Pathfinder {
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),
t => t =>
AdditionalTileFilter(t) && AdditionalTileFilter(t) &&
(!distances.ContainsKey(t) || distances[t] > distances[tile]) &&
(!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));
@ -36,4 +41,42 @@ public abstract class RoamingPathfinder : Pathfinder {
neighbours.ForEach(t => CrawlStep(t, distances)); neighbours.ForEach(t => CrawlStep(t, distances));
} }
} }
public override Decision DecideNext(MapTile goal) {
Dictionary<MapTile, int> distances = Crawl(goal);
List<MapTile> closerTiles = GetNeighbours(Enemy.Location,
c => AdditionalConnectorFilter(c) && !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,
t => AdditionalTileFilter(t) && distances.ContainsKey(t) && distances[t] + t.GetConnector(Enemy.Location).Value <= distances[Enemy.Location] + tolerance);
if (closerTiles.Contains(goal)){
if (Enemy.Location.GetConnector(goal)!.Blocked){
return Decision.Reset();
}
IEnumerable<ServerPlayer> players = Server.Players.Values.Where(p => p.state.officeTileId == goal.Id);
if (players.Count() == 1){
return Decision.Attack(players.First());
}
return Decision.Move(goal);
}
if (closerTiles.Count != 0 && closerTiles.All(t => TakenPath.Contains(t))){
TakenPath.Clear();
return DecideNext(goal);
}
closerTiles.RemoveAll(t => TakenPath.Contains(t));
closerTiles.ForEach(t => distances[t] += Enemy.Location.GetConnector(t)!.Value);
double roll = random.NextDouble() * closerTiles.Sum(t => 1.0 / distances[t]);
foreach (var tile in closerTiles){
double value = 1.0 / distances[tile];
if (roll <= value){
TakenPath.Add(tile);
return Decision.Move(tile);
}
roll -= value;
}
return Decision.Wait();
}
} }

View file

@ -21,6 +21,8 @@ public class GameLogic {
public static int RemoteDoorUsage{ get; set; } = 3; public static int RemoteDoorUsage{ get; set; } = 3;
public static int LightUsage{ get; set; } = 2; public static int LightUsage{ get; set; } = 2;
private static bool unlimitedPower = false; // 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;
// public static int P2Power{ get; set; } = Power.MAX_POWER_VALUE; // public static int P2Power{ get; set; } = Power.MAX_POWER_VALUE;
@ -64,11 +66,11 @@ public class GameLogic {
secondCycleTimer.Start(); secondCycleTimer.Start();
Server.SendUpdateToAll([GameEvent.GAME_START()]); Server.SendUpdateToAll([GameEvent.GAME_START()]);
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(10)).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(0)).Spawn(MapManager.Get(22)); // EnemyManager.AddEnemy(new MareEnemy(10)).Spawn(MapManager.Get(22));
} }
public static void Update() { public static void Update() {
if(secondCycleTimer.Check()) NSecondUpdate = true; if(secondCycleTimer.Check()) NSecondUpdate = true;
@ -87,6 +89,11 @@ public class GameLogic {
// Server.SendUpdateToAll([GameEvent.POWER_OUT(player.state.pid)]); // Server.SendUpdateToAll([GameEvent.POWER_OUT(player.state.pid)]);
} }
if (unlimitedPower){ // Debug
Server.P1.state.power = Power.MAX_POWER_VALUE;
Server.P2.state.power = Power.MAX_POWER_VALUE;
}
Server.SendUpdateToAll([GameEvent.POWER_TICK(Server.P1.state.pid, Server.P1.state.power), GameEvent.POWER_TICK(Server.P2.state.pid, Server.P2.state.power)]); Server.SendUpdateToAll([GameEvent.POWER_TICK(Server.P1.state.pid, Server.P1.state.power), GameEvent.POWER_TICK(Server.P2.state.pid, Server.P2.state.power)]);
} }

View file

@ -19,7 +19,7 @@ public static class MapManager {
[(4, 0)] =[(4, 1, 1, ConnectorType.DOOR_REMOTE)], [(4, 0)] =[(4, 1, 1, ConnectorType.DOOR_REMOTE)],
[(0, 1)] =[(1, 1, 1, ConnectorType.HALL)], [(0, 1)] =[(1, 1, 1, ConnectorType.HALL)],
[(1, 1)] =[(1, 2, 1, ConnectorType.DOOR_REMOTE)], [(1, 1)] =[(1, 2, 1, ConnectorType.DOOR_REMOTE)],
[(2, 1)] =[(2, 2, 1, ConnectorType.HALL), (2, 0, 1, ConnectorType.DOOR_OFFICE)], [(2, 1)] =[(2, 2, 2, ConnectorType.HALL), (2, 0, 1, ConnectorType.DOOR_OFFICE)],
[(3, 1)] =[(3, 2, 1, ConnectorType.DOOR_REMOTE), (4, 1, 1, ConnectorType.HALL)] [(3, 1)] =[(3, 2, 1, ConnectorType.DOOR_REMOTE), (4, 1, 1, ConnectorType.HALL)]
}; };
@ -30,6 +30,8 @@ public static class MapManager {
[(3,2)] = [(4,2,1)] [(3,2)] = [(4,2,1)]
}; };
private static (int x, int y)[] ventTiles =[(0, 1), (4, 1), (2, 2), (0, 3), (4, 3)];
public static void InitMap() { public static void InitMap() {
for (int i = 0; i < 5; i++){ for (int i = 0; i < 5; i++){
for (int j = 0; j < 2; j++){ for (int j = 0; j < 2; j++){
@ -56,6 +58,15 @@ public static class MapManager {
doors = GetAllConnectors().Where(c => c.Type == ConnectorType.DOOR_OFFICE || c.Type == ConnectorType.DOOR_REMOTE).ToList(); doors = GetAllConnectors().Where(c => c.Type == ConnectorType.DOOR_OFFICE || c.Type == ConnectorType.DOOR_REMOTE).ToList();
doorsP1 = doors.Where(c => c.Owner == Server.P1).ToList(); doorsP1 = doors.Where(c => c.Owner == Server.P1).ToList();
doorsP2 = doors.Where(c => c.Owner == Server.P2).ToList(); doorsP2 = doors.Where(c => c.Owner == Server.P2).ToList();
foreach (var tile1Coords in ventTiles){
MapTile tile1 = map[tile1Coords.x, tile1Coords.y];
foreach (var tile2Coords in ventTiles){
MapTile tile2 = map[tile2Coords.x, tile2Coords.y];
if(tile1.GetConnector(tile2) != null || tile1 == tile2) continue;
tile1.AddConnector(new TileConnector(tile2, ConnectorType.VENT, 2));
}
}
} }
public static TileConnector[] GetAllConnectors() { public static TileConnector[] GetAllConnectors() {

View file

@ -25,6 +25,7 @@ public struct GameEvent : INetSerializable{
public static GameEvent POWER_OUT(int pid) => new(){ID = 14, Args = [pid]}; public static GameEvent POWER_OUT(int pid) => new(){ID = 14, Args = [pid]};
public static GameEvent TOGGLE_LIGHT(int pid, int camId, bool state) => new(){ID = 15, Args = [pid, camId, state ? 1 : 0]}; public static GameEvent TOGGLE_LIGHT(int pid, int camId, bool state) => new(){ID = 15, Args = [pid, camId, state ? 1 : 0]};
public static GameEvent NEKO_ANGERED(int pid, int enemyId) => new(){ID = 16, Args = [pid, enemyId]}; public static GameEvent NEKO_ANGERED(int pid, int enemyId) => new(){ID = 16, Args = [pid, enemyId]};
public static GameEvent VENT_USED() => new(){ID = 17};
public int ID{ get; set; } public int ID{ get; set; }
public bool Hideable => ID < 0; public bool Hideable => ID < 0;