diff --git a/FNAF_Clone.sln.DotSettings.user b/FNAF_Clone.sln.DotSettings.user
index 5e3ead0..e95bd55 100644
--- a/FNAF_Clone.sln.DotSettings.user
+++ b/FNAF_Clone.sln.DotSettings.user
@@ -5,6 +5,7 @@
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
diff --git a/FNAF_Clone/Client.cs b/FNAF_Clone/Client.cs
index f37260a..b44d782 100644
--- a/FNAF_Clone/Client.cs
+++ b/FNAF_Clone/Client.cs
@@ -37,7 +37,7 @@ public class Client {
public static ClientPlayer Opponent { get; } = new();
public static ClientPlayer GetPlayer(int pid) => Player.state.pid == pid ? Player : Opponent;
-
+
public static void Connect(string endPoint, int port) {
State = ConnectionState.CONNECTING;
writer = new NetDataWriter();
diff --git a/FNAF_Clone/CommandManager.cs b/FNAF_Clone/CommandManager.cs
index 6487893..8c77e92 100644
--- a/FNAF_Clone/CommandManager.cs
+++ b/FNAF_Clone/CommandManager.cs
@@ -34,6 +34,7 @@ public class CommandManager {
Client.Player.state.monitorUp = !Client.Player.state.monitorUp;
UIManager.ChangeMonitorState(Client.Player.state.monitorUp);
Client.SendCommands([PlayerCommand.SET_MONITOR(Client.Player.state.monitorUp)]);
+ SoundManager.PlayMonitorFlip();
}
private static void ToggleDoorLeft() => SendToggleDoor(Direction.EAST);
@@ -54,6 +55,7 @@ public class CommandManager {
if (direction == Direction.SOUTH) return;
Client.Player.state.doorStates[(int)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])]);
}
@@ -63,6 +65,7 @@ public class CommandManager {
connector.Blocked = !connector.Blocked;
Client.SendCommands([PlayerCommand.SET_DOOR_REMOTE(connector.Id, connector.Blocked)]);
+ SoundManager.PlayDoorRemote(connector.Blocked);
UIManager.ChangeRemoteDoorState(connector.Id, connector.Blocked);
// Console.WriteLine("Changed door state to: " + (connector.Blocked ? "blocked" : "open") + " for door " + connector);
// add UIManager toggle door
@@ -73,6 +76,7 @@ public class CommandManager {
Client.Player.state.camera = id;
UIManager.ChangeCamera(id);
+ SoundManager.PlayCameraSwitch();
Client.SendCommands([PlayerCommand.SWITCH_CAM(id)]);
MapTileProjection tile = ClientMapManager.Get(id);
// add UIManager switch camera
@@ -88,6 +92,7 @@ public class CommandManager {
if(!Client.Player.state.monitorUp || ClientMapManager.Get(id).Owner != Client.Player) return;
ClientMapManager.Get(id).Lit = state;
UIManager.UpdateCameras([id]);
+ SoundManager.PlayLight(state);
Client.SendCommands([PlayerCommand.SET_LIGHT(id, state)]);
}
}
\ No newline at end of file
diff --git a/FNAF_Clone/Content/Content.mgcb b/FNAF_Clone/Content/Content.mgcb
index 32d5772..65915be 100644
--- a/FNAF_Clone/Content/Content.mgcb
+++ b/FNAF_Clone/Content/Content.mgcb
@@ -95,3 +95,123 @@
/processorParam:TextureFormat=Compressed
/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
+
diff --git a/FNAF_Clone/Content/sounds/ambience.wav b/FNAF_Clone/Content/sounds/ambience.wav
new file mode 100644
index 0000000..4750c1e
Binary files /dev/null and b/FNAF_Clone/Content/sounds/ambience.wav differ
diff --git a/FNAF_Clone/Content/sounds/camera-switch.wav b/FNAF_Clone/Content/sounds/camera-switch.wav
new file mode 100644
index 0000000..4e53b39
Binary files /dev/null and b/FNAF_Clone/Content/sounds/camera-switch.wav differ
diff --git a/FNAF_Clone/Content/sounds/dash-move.wav b/FNAF_Clone/Content/sounds/dash-move.wav
new file mode 100644
index 0000000..727130b
Binary files /dev/null and b/FNAF_Clone/Content/sounds/dash-move.wav differ
diff --git a/FNAF_Clone/Content/sounds/dash-sounds-raw.wav b/FNAF_Clone/Content/sounds/dash-sounds-raw.wav
new file mode 100644
index 0000000..76bf6ed
Binary files /dev/null and b/FNAF_Clone/Content/sounds/dash-sounds-raw.wav differ
diff --git a/FNAF_Clone/Content/sounds/door-close-remote.wav b/FNAF_Clone/Content/sounds/door-close-remote.wav
new file mode 100644
index 0000000..736b61e
Binary files /dev/null and b/FNAF_Clone/Content/sounds/door-close-remote.wav differ
diff --git a/FNAF_Clone/Content/sounds/door-close.wav b/FNAF_Clone/Content/sounds/door-close.wav
new file mode 100644
index 0000000..c284ed3
Binary files /dev/null and b/FNAF_Clone/Content/sounds/door-close.wav differ
diff --git a/FNAF_Clone/Content/sounds/door-open-remote.wav b/FNAF_Clone/Content/sounds/door-open-remote.wav
new file mode 100644
index 0000000..2fb60c9
Binary files /dev/null and b/FNAF_Clone/Content/sounds/door-open-remote.wav differ
diff --git a/FNAF_Clone/Content/sounds/door-open.wav b/FNAF_Clone/Content/sounds/door-open.wav
new file mode 100644
index 0000000..fea78ba
Binary files /dev/null and b/FNAF_Clone/Content/sounds/door-open.wav differ
diff --git a/FNAF_Clone/Content/sounds/jumpscare.wav b/FNAF_Clone/Content/sounds/jumpscare.wav
new file mode 100644
index 0000000..09f7267
Binary files /dev/null and b/FNAF_Clone/Content/sounds/jumpscare.wav differ
diff --git a/FNAF_Clone/Content/sounds/light-off.wav b/FNAF_Clone/Content/sounds/light-off.wav
new file mode 100644
index 0000000..6c1f9ac
Binary files /dev/null and b/FNAF_Clone/Content/sounds/light-off.wav differ
diff --git a/FNAF_Clone/Content/sounds/light-on.wav b/FNAF_Clone/Content/sounds/light-on.wav
new file mode 100644
index 0000000..a5b6b88
Binary files /dev/null and b/FNAF_Clone/Content/sounds/light-on.wav differ
diff --git a/FNAF_Clone/Content/sounds/mare-move.wav b/FNAF_Clone/Content/sounds/mare-move.wav
new file mode 100644
index 0000000..5965d1f
Binary files /dev/null and b/FNAF_Clone/Content/sounds/mare-move.wav differ
diff --git a/FNAF_Clone/Content/sounds/monitor-flip.wav b/FNAF_Clone/Content/sounds/monitor-flip.wav
new file mode 100644
index 0000000..cde0929
Binary files /dev/null and b/FNAF_Clone/Content/sounds/monitor-flip.wav differ
diff --git a/FNAF_Clone/Content/sounds/neko-angry.wav b/FNAF_Clone/Content/sounds/neko-angry.wav
new file mode 100644
index 0000000..6a317ca
Binary files /dev/null and b/FNAF_Clone/Content/sounds/neko-angry.wav differ
diff --git a/FNAF_Clone/Content/sounds/neko-move1.wav b/FNAF_Clone/Content/sounds/neko-move1.wav
new file mode 100644
index 0000000..52a53c8
Binary files /dev/null and b/FNAF_Clone/Content/sounds/neko-move1.wav differ
diff --git a/FNAF_Clone/Content/sounds/neko-purr.wav b/FNAF_Clone/Content/sounds/neko-purr.wav
new file mode 100644
index 0000000..9d9bb1b
Binary files /dev/null and b/FNAF_Clone/Content/sounds/neko-purr.wav differ
diff --git a/FNAF_Clone/Content/sounds/powerout.wav b/FNAF_Clone/Content/sounds/powerout.wav
new file mode 100644
index 0000000..e69ce46
Binary files /dev/null and b/FNAF_Clone/Content/sounds/powerout.wav differ
diff --git a/FNAF_Clone/Content/sounds/spot-activate.wav b/FNAF_Clone/Content/sounds/spot-activate.wav
new file mode 100644
index 0000000..dfadecc
Binary files /dev/null and b/FNAF_Clone/Content/sounds/spot-activate.wav differ
diff --git a/FNAF_Clone/Content/sounds/spot-move.wav b/FNAF_Clone/Content/sounds/spot-move.wav
new file mode 100644
index 0000000..aee503f
Binary files /dev/null and b/FNAF_Clone/Content/sounds/spot-move.wav differ
diff --git a/FNAF_Clone/Content/sounds/vent-walk.wav b/FNAF_Clone/Content/sounds/vent-walk.wav
new file mode 100644
index 0000000..a256e06
Binary files /dev/null and b/FNAF_Clone/Content/sounds/vent-walk.wav differ
diff --git a/FNAF_Clone/EventProcessor.cs b/FNAF_Clone/EventProcessor.cs
index 21c86dc..c242c25 100644
--- a/FNAF_Clone/EventProcessor.cs
+++ b/FNAF_Clone/EventProcessor.cs
@@ -77,25 +77,29 @@ public class EventProcessor {
int oldPos = ClientEnemyManager.Get(e.Args[0]).Location!.Id;
ClientEnemyManager.Move(e.Args[0], ClientMapManager.Get(e.Args[1]));
UIManager.UpdateCameras([oldPos, e.Args[1]]);
+ SoundManager.PlayEnemyMove(ClientEnemyManager.Get(e.Args[0]));
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]));
+ SoundManager.PlayJumpscare();
}
break;
- case 9:
+ case 9: // reset
Console.WriteLine($"E: Enemy {e.Args[0]} reset to {e.Args[1]}");
int preResetPos = ClientEnemyManager.Get(e.Args[0]).Location!.Id;
ClientEnemyManager.Move(e.Args[0], ClientMapManager.Get(e.Args[1]));
UIManager.UpdateCameras([preResetPos, e.Args[1]]);
+ SoundManager.PlayEnemyReset(ClientEnemyManager.Get(e.Args[0]));
break;
case 10:
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);
+ SoundManager.PlaySpotActivate();
break;
case 11:
@@ -103,10 +107,11 @@ public class EventProcessor {
if(Client.Player.state.pid == e.Args[0]) UIManager.ShowVictoryScreen();
break;
- case 12:
+ case 12: // game start
Console.WriteLine("E: Game started");
UIManager.DisplayGameUI();
UIManager.StartTimer();
+ SoundManager.StartAmbience();
break;
case 13:
@@ -140,6 +145,7 @@ public class EventProcessor {
UIManager.ChangeDoorState(Direction.WEST, false);
CommandManager.AllowGameControls(false);
UIManager.ChangeMonitorState(false);
+ SoundManager.PlayPowerOut();
}
else{
UIManager.ChangeDoorStateOpponent(Direction.EAST, false);
@@ -159,8 +165,17 @@ public class EventProcessor {
}
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;
+
}
}
}
diff --git a/FNAF_Clone/GUI/UIManager.cs b/FNAF_Clone/GUI/UIManager.cs
index 5cf1de3..99864f9 100644
--- a/FNAF_Clone/GUI/UIManager.cs
+++ b/FNAF_Clone/GUI/UIManager.cs
@@ -49,6 +49,8 @@ public class UIManager {
private static InputListenerHook monitorSwitchHook;
+ private static bool fullBright = false; // Debug
+
public static void LoadAssets() {
testAtlas = TextureAtlas.FromFile(Core.content, "images/testBlocks-definition.xml");
OfficeAtlas = TextureAtlas.FromFile(Core.content, "images/office-definition.xml");
@@ -202,7 +204,7 @@ public class UIManager {
int stateInt = state ? 1 : 0;
switch ((int)dir){
- case 0:
+ case 0:
officeScreen["office_left"].SetTexture(stateInt);
monitorScreen["p1-office-door-left"].SetTexture(stateInt);
break;
@@ -214,7 +216,7 @@ public class UIManager {
officeScreen["office_right"].SetTexture(stateInt);
monitorScreen["p1-office-door-right"].SetTexture(stateInt);
break;
- }
+ }
}
public static void ChangeDoorStateOpponent(Direction dir, bool state) { // TODO: overload to avoid excessive casting
@@ -239,6 +241,7 @@ public class UIManager {
public static void ChangeMonitorState(bool state) {
Screen.SetScreen(state ? ScreenTypes.CAMERAS : ScreenTypes.OFFICE);
+ UpdateCameras([Client.Player.state.camera]);
}
public static void ChangeMonitorStateOpponent(bool state) {
@@ -259,7 +262,7 @@ public class UIManager {
}
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;
enemyElements.Values.Where(e => e.Visible).ToList().ForEach(e => e.Visible = false);
ClientEnemy[] enemies = ClientEnemyManager.GetByLocation(ClientMapManager.Get(Client.Player.state.camera));
@@ -270,6 +273,13 @@ public class UIManager {
enemyElement.Visible = true;
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.DisableOverlay();
CommandManager.AllowGameControls(false);
+ SoundManager.StopAmbience();
InputManager.AddListener(Keys.Space, DisplayMainMenu, InputTiming.PRESS, new InputListenerHook(true, true));
}
@@ -300,6 +311,7 @@ public class UIManager {
Screen.SetScreen(ScreenTypes.LOSE);
Screen.DisableOverlay();
CommandManager.AllowGameControls(false);
+ SoundManager.StopAmbience();
InputManager.AddListener(Keys.Space, DisplayMainMenu, InputTiming.PRESS, new InputListenerHook(true, true));
}
diff --git a/FNAF_Clone/GameMain.cs b/FNAF_Clone/GameMain.cs
index 362a266..0b5cac1 100644
--- a/FNAF_Clone/GameMain.cs
+++ b/FNAF_Clone/GameMain.cs
@@ -29,6 +29,7 @@ public class GameMain() : Core("fnafkooo", 1280, 720, false) {
protected override void LoadContent() {
UIManager.LoadAssets();
+ SoundManager.LoadSounds();
// spriteBatch = new SpriteBatch(GraphicsDevice);
// font = Content.Load("font");
}
diff --git a/FNAF_Clone/SoundManager.cs b/FNAF_Clone/SoundManager.cs
new file mode 100644
index 0000000..28ce4fc
--- /dev/null
+++ b/FNAF_Clone/SoundManager.cs
@@ -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("sounds/ambience");
+ ventWalk = Core.content.Load("sounds/vent-walk");
+ camSwitch = Core.content.Load("sounds/camera-switch");
+ lightOff = Core.content.Load("sounds/light-off");
+ lightOn = Core.content.Load("sounds/light-on");
+ doorClose = Core.content.Load("sounds/door-close");
+ doorCloseRemote = Core.content.Load("sounds/door-close-remote");
+ doorOpen = Core.content.Load("sounds/door-open");
+ doorOpenRemote = Core.content.Load("sounds/door-open-remote");
+ monitorFlip = Core.content.Load("sounds/monitor-flip");
+ powerOut = Core.content.Load("sounds/powerout");
+ mareMove = Core.content.Load("sounds/mare-move");
+ dashMove = Core.content.Load("sounds/dash-move");
+ spotMove = Core.content.Load("sounds/spot-move");
+ spotActivate = Core.content.Load("sounds/spot-activate");
+ nekoPurr = Core.content.Load("sounds/neko-purr");
+ jumpscare = Core.content.Load("sounds/jumpscare");
+ nekoAnger = Core.content.Load("sounds/neko-angry");
+ nekoMove = Core.content.Load("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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FNAF_Server/Enemies/ITargetingEnemy.cs b/FNAF_Server/Enemies/ITargetingEnemy.cs
new file mode 100644
index 0000000..07be438
--- /dev/null
+++ b/FNAF_Server/Enemies/ITargetingEnemy.cs
@@ -0,0 +1,5 @@
+namespace FNAF_Server.Enemies;
+
+public interface ITargetingEnemy {
+ ServerPlayer Target { get; set; }
+}
\ No newline at end of file
diff --git a/FNAF_Server/Enemies/LurkEnemy.cs b/FNAF_Server/Enemies/LurkEnemy.cs
index 273581d..f7fe077 100644
--- a/FNAF_Server/Enemies/LurkEnemy.cs
+++ b/FNAF_Server/Enemies/LurkEnemy.cs
@@ -32,13 +32,13 @@ public class LurkEnemy : Enemy {
base.Spawn(location);
// stopwatch.Start();
movementOpportunity.Start();
- pathfinder.takenPath.Add(Location);
+ pathfinder.TakenPath.Add(Location);
Server.SendUpdateToAll([GameEvent.ENEMY_SPAWN(TypeId, Id, Difficulty, Location.Id)]);
}
public override void Reset() {
- pathfinder.takenPath.Clear();
- pathfinder.takenPath.Add(Location);
+ pathfinder.TakenPath.Clear();
+ pathfinder.TakenPath.Add(Location);
Target.state.power -= 30;
@@ -69,6 +69,9 @@ public class LurkEnemy : Enemy {
Pathfinder.Decision decision = pathfinder.DecideNext(MapManager.Get(Target.state.officeTileId));
switch (decision.type){
case Pathfinder.Decision.MoveType:
+ if (Location.GetConnector(decision.nextTile!)!.Type == ConnectorType.VENT){
+ Server.SendUpdateToAll([GameEvent.VENT_USED()]);
+ }
Location = decision.nextTile!;
Server.SendUpdateToAll([GameEvent.ENEMY_MOVEMENT(Id, Location.Id)]);
Console.WriteLine($"Enemy {TypeId} ({Name}) moving to {Location.PositionAsString})");
@@ -86,46 +89,43 @@ public class LurkEnemy : Enemy {
}
private class LurkPathfinder : RoamingPathfinder {
- private Random random = new();
+ // private Random random = new();
- private int tolerance;
- public List takenPath = new();
-
- public LurkPathfinder(Enemy enemy, int tolerance) : base(enemy) {
+ public LurkPathfinder(Enemy enemy, int tolerance) : base(enemy, tolerance) {
this.tolerance = tolerance;
}
- public override Decision DecideNext(MapTile goal) {
- Dictionary distances = Crawl(goal);
-
- List closerTiles = GetNeighbours(Enemy.Location,
- c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,
- t => distances.ContainsKey(t) && distances[t] <= distances[Enemy.Location] + tolerance);
- if (closerTiles.Contains(goal)){
- if (Enemy.Location.GetConnector(goal)!.Blocked){
- return Decision.Reset();
- }
-
- return Decision.Attack(((LurkEnemy)Enemy).Target);
- }
-
- 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){
- if (roll <= 1.0 / distances[tile]){
- takenPath.Add(tile);
- return Decision.Move(tile);
- }
- roll -= 1.0 / distances[tile];
- }
-
- return Decision.Wait();
- }
+ // public override Decision DecideNext(MapTile goal) {
+ // Dictionary distances = Crawl(goal);
+ //
+ // List closerTiles = GetNeighbours(Enemy.Location,
+ // c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,
+ // t => distances.ContainsKey(t) && distances[t] <= distances[Enemy.Location] + tolerance);
+ // if (closerTiles.Contains(goal)){
+ // if (Enemy.Location.GetConnector(goal)!.Blocked){
+ // return Decision.Reset();
+ // }
+ //
+ // return Decision.Attack(((LurkEnemy)Enemy).Target);
+ // }
+ //
+ // 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){
+ // if (roll <= 1.0 / distances[tile]){
+ // takenPath.Add(tile);
+ // return Decision.Move(tile);
+ // }
+ // roll -= 1.0 / distances[tile];
+ // }
+ //
+ // return Decision.Wait();
+ // }
}
}
\ No newline at end of file
diff --git a/FNAF_Server/Enemies/MareEnemy.cs b/FNAF_Server/Enemies/MareEnemy.cs
index ced2b3a..91f9a1c 100644
--- a/FNAF_Server/Enemies/MareEnemy.cs
+++ b/FNAF_Server/Enemies/MareEnemy.cs
@@ -31,7 +31,7 @@ public class MareEnemy : Enemy {
}
public override void Reset() {
- pathfinder.takenPath.Clear();
+ pathfinder.TakenPath.Clear();
Target = Server.OtherPlayer(Target);
Pathfinder.Decision decision = pathfinder.DecideNext(MapManager.Get(Target.state.officeTileId));
if (decision.type == Pathfinder.Decision.MoveType){
@@ -53,7 +53,7 @@ public class MareEnemy : Enemy {
// stopwatch.Start();
movementOpportunity.Start();
- pathfinder.takenPath.Add(Location);
+ pathfinder.TakenPath.Add(Location);
Server.SendUpdateToAll([GameEvent.ENEMY_SPAWN(TypeId, Id, Difficulty, Location.Id)]);
}
@@ -87,46 +87,44 @@ public class MareEnemy : Enemy {
private class MarePathfinder : RoamingPathfinder {
- private Random random = new();
-
- private int tolerance;
- public List takenPath = new();
-
- public MarePathfinder(Enemy enemy, int tolerance) : base(enemy) {
+ // private Random random = new();
+
+ public MarePathfinder(Enemy enemy, int tolerance) : base(enemy, tolerance) {
this.tolerance = tolerance;
+ AdditionalConnectorFilter = c => c.Type != ConnectorType.VENT;
}
- public override Decision DecideNext(MapTile goal) {
- Dictionary distances = Crawl(goal);
-
- List closerTiles = GetNeighbours(Enemy.Location,
- c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,
- t => distances.ContainsKey(t) && distances[t] <= distances[Enemy.Location] + tolerance);
- if (closerTiles.Contains(goal)){
- if (Enemy.Location.GetConnector(goal)!.Blocked){
- return Decision.Reset();
- }
-
- return Decision.Attack(((MareEnemy)Enemy).Target);
- }
-
- 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){
- if (roll <= 1.0 / distances[tile]){
- takenPath.Add(tile);
- return Decision.Move(tile);
- }
- roll -= 1.0 / distances[tile];
- }
-
- return Decision.Wait();
- }
+ // public override Decision DecideNext(MapTile goal) {
+ // Dictionary distances = Crawl(goal);
+ //
+ // List closerTiles = GetNeighbours(Enemy.Location,
+ // c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,
+ // t => distances.ContainsKey(t) && distances[t] <= distances[Enemy.Location] + tolerance);
+ // if (closerTiles.Contains(goal)){
+ // if (Enemy.Location.GetConnector(goal)!.Blocked){
+ // return Decision.Reset();
+ // }
+ //
+ // return Decision.Attack(((MareEnemy)Enemy).Target);
+ // }
+ //
+ // 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){
+ // if (roll <= 1.0 / distances[tile]){
+ // takenPath.Add(tile);
+ // return Decision.Move(tile);
+ // }
+ // roll -= 1.0 / distances[tile];
+ // }
+ //
+ // return Decision.Wait();
+ // }
}
}
\ No newline at end of file
diff --git a/FNAF_Server/Enemies/NekoEnemy.cs b/FNAF_Server/Enemies/NekoEnemy.cs
index 0c41afe..1f4b492 100644
--- a/FNAF_Server/Enemies/NekoEnemy.cs
+++ b/FNAF_Server/Enemies/NekoEnemy.cs
@@ -6,10 +6,10 @@ namespace FNAF_Server.Enemies;
public class NekoEnemy : Enemy {
private MovementOpportunity movementOpportunity;
- private NekoPathfinder pathfinder;
+ private RoamingPathfinder pathfinder;
- public NekoEnemy(int difficulty) : base(difficulty) {
- pathfinder = new NekoPathfinder(this, 1);
+ public NekoEnemy(int difficulty) : base(difficulty) {
+ pathfinder = new RoamingPathfinder(this, 1){AdditionalTileFilter = t => Aggressive || t.Owner == null || !t.Lit};
movementOpportunity = new MovementOpportunity(5000);
SetDifficulty(difficulty);
@@ -28,15 +28,16 @@ public class NekoEnemy : Enemy {
base.Spawn(location);
// stopwatch.Start();
movementOpportunity.Start();
- pathfinder.takenPath.Add(Location);
+ pathfinder.TakenPath.Add(Location);
Server.SendUpdateToAll([GameEvent.ENEMY_SPAWN(TypeId, Id, Difficulty, Location.Id)]);
}
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();
Location = resetLocations[new Random().Next(resetLocations.Length)];
Aggressive = false;
+ Target = Server.OtherPlayer(Target);
Server.SendUpdateToAll([GameEvent.ENEMY_RESET(Id, Location.Id)]);
}
@@ -49,7 +50,7 @@ public class NekoEnemy : Enemy {
public override void Update() {
base.Update();
- if (Location.Owner != null && Location.Lit){
+ if (Location.Owner != null && Location.Lit && !Aggressive){
Aggressive = true;
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));
switch (decision.type){
case Pathfinder.Decision.MoveType:
+ if (Location.GetConnector(decision.nextTile!)!.Type == ConnectorType.VENT){
+ Server.SendUpdateToAll([GameEvent.VENT_USED()]);
+ }
Location = decision.nextTile!;
Server.SendUpdateToAll([GameEvent.ENEMY_MOVEMENT(Id, Location.Id)]);
Console.WriteLine($"Enemy {TypeId} ({Name}) moving to {Location.PositionAsString})");
@@ -88,49 +92,46 @@ public class NekoEnemy : Enemy {
}
private class NekoPathfinder : RoamingPathfinder {
- private Random random = new();
+ // private Random random = new();
- private int tolerance;
- public List takenPath = new();
-
- public NekoPathfinder(Enemy enemy, int tolerance) : base(enemy) {
+ public NekoPathfinder(Enemy enemy, int tolerance) : base(enemy, tolerance) {
this.tolerance = tolerance;
AdditionalTileFilter = t => ((NekoEnemy)Enemy).Aggressive || t.Owner == null || !t.Lit;
}
- public override Decision DecideNext(MapTile goal) {
- Dictionary distances = Crawl(goal);
-
- List closerTiles = GetNeighbours(Enemy.Location,
- c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,
- t =>
- distances.ContainsKey(t) &&
- distances[t] <= distances[Enemy.Location] + tolerance);
- if (closerTiles.Contains(goal)){
- if (Enemy.Location.GetConnector(goal)!.Blocked){
- return Decision.Reset();
- }
-
- return Decision.Attack(((NekoEnemy)Enemy).Target);
- }
-
- 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){
- if (roll <= 1.0 / distances[tile]){
- takenPath.Add(tile);
- return Decision.Move(tile);
- }
- roll -= 1.0 / distances[tile];
- }
-
- return Decision.Wait();
- }
+ // public override Decision DecideNext(MapTile goal) {
+ // Dictionary distances = Crawl(goal);
+ //
+ // List closerTiles = GetNeighbours(Enemy.Location,
+ // c => !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE,
+ // t =>
+ // distances.ContainsKey(t) &&
+ // distances[t] <= distances[Enemy.Location] + tolerance);
+ // if (closerTiles.Contains(goal)){
+ // if (Enemy.Location.GetConnector(goal)!.Blocked){
+ // return Decision.Reset();
+ // }
+ //
+ // return Decision.Attack(((NekoEnemy)Enemy).Target);
+ // }
+ //
+ // 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){
+ // if (roll <= 1.0 / distances[tile]){
+ // takenPath.Add(tile);
+ // return Decision.Move(tile);
+ // }
+ // roll -= 1.0 / distances[tile];
+ // }
+ //
+ // return Decision.Wait();
+ // }
}
}
\ No newline at end of file
diff --git a/FNAF_Server/Enemies/RoamingPathfinder.cs b/FNAF_Server/Enemies/RoamingPathfinder.cs
index 3b43dd2..185c765 100644
--- a/FNAF_Server/Enemies/RoamingPathfinder.cs
+++ b/FNAF_Server/Enemies/RoamingPathfinder.cs
@@ -3,13 +3,18 @@ using GlobalClassLib;
namespace FNAF_Server.Enemies;
-public abstract class RoamingPathfinder : Pathfinder {
+public class RoamingPathfinder : Pathfinder {
- protected Predicate AdditionalTileFilter = _ => true;
- protected Predicate AdditionalConnectorFilter = _ => true;
+ public Predicate AdditionalTileFilter{ get; set; } = _ => true;
+ public Predicate AdditionalConnectorFilter{ get; set; } = _ => true;
+ protected int tolerance;
+ public List TakenPath { get; } = new();
+
+ private Random random = new();
// private Func defaultConnectorFilter;
- protected RoamingPathfinder(Enemy enemy) : base(enemy) {
+ public RoamingPathfinder(Enemy enemy, int tolerance) : base(enemy) {
+ this.tolerance = tolerance;
}
protected Dictionary Crawl(MapTile tile) {
@@ -23,10 +28,10 @@ public abstract class RoamingPathfinder : Pathfinder {
c =>
AdditionalConnectorFilter(c) &&
(!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 =>
AdditionalTileFilter(t) &&
- (!distances.ContainsKey(t) || distances[t] > distances[tile]) &&
(!Enemy.BlocksTile || EnemyManager.GetByLocation(t).All(e => !e.BlocksTile || e == Enemy)) &&
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));
}
}
+
+ public override Decision DecideNext(MapTile goal) {
+ Dictionary distances = Crawl(goal);
+
+ List 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 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();
+ }
}
\ No newline at end of file
diff --git a/FNAF_Server/GameLogic.cs b/FNAF_Server/GameLogic.cs
index 0cf9d65..6c2cef6 100644
--- a/FNAF_Server/GameLogic.cs
+++ b/FNAF_Server/GameLogic.cs
@@ -20,6 +20,8 @@ public class GameLogic {
public static int OfficeDoorUsage{ get; set; } = 10;
public static int RemoteDoorUsage{ get; set; } = 3;
public static int LightUsage{ get; set; } = 2;
+
+ private static bool unlimitedPower = false; // Debug
// public const int POWER_MAX = 1000;
// public static int P1Power{ get; set; } = Power.MAX_POWER_VALUE;
@@ -64,11 +66,11 @@ public class GameLogic {
secondCycleTimer.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 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 MareEnemy(0)).Spawn(MapManager.Get(22));
+ // EnemyManager.AddEnemy(new MareEnemy(10)).Spawn(MapManager.Get(22));
}
public static void Update() {
if(secondCycleTimer.Check()) NSecondUpdate = true;
@@ -86,7 +88,12 @@ public class GameLogic {
}
// 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)]);
}
diff --git a/FNAF_Server/Map/MapManager.cs b/FNAF_Server/Map/MapManager.cs
index 5104316..7c43ab1 100644
--- a/FNAF_Server/Map/MapManager.cs
+++ b/FNAF_Server/Map/MapManager.cs
@@ -19,7 +19,7 @@ public static class MapManager {
[(4, 0)] =[(4, 1, 1, ConnectorType.DOOR_REMOTE)],
[(0, 1)] =[(1, 1, 1, ConnectorType.HALL)],
[(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)]
};
@@ -29,6 +29,8 @@ public static class MapManager {
[(2,2)] = [(3,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() {
for (int i = 0; i < 5; i++){
@@ -56,6 +58,15 @@ public static class MapManager {
doors = GetAllConnectors().Where(c => c.Type == ConnectorType.DOOR_OFFICE || c.Type == ConnectorType.DOOR_REMOTE).ToList();
doorsP1 = doors.Where(c => c.Owner == Server.P1).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() {
diff --git a/PacketLib/GameEvent.cs b/PacketLib/GameEvent.cs
index 76b0590..8d07c33 100644
--- a/PacketLib/GameEvent.cs
+++ b/PacketLib/GameEvent.cs
@@ -25,6 +25,7 @@ public struct GameEvent : INetSerializable{
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 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 bool Hideable => ID < 0;