Repository: maxk2807/WaterWizards
Pull request #206: Refactor asset paths and enhance PlacedShip functionality with damage…
Author: login: Erickk0
Created: 2025-05-25T11:15:01Z | Merged: 2025-05-26T14:37:52Z

This pull request introduces several changes to improve the functionality and organization of the `WaterWizard` project. The key updates include refactoring the `PlacedShip` class, enhancing the `HandleAttack` method for better game logic, and adjusting asset paths for consistency. Below is a breakdown of the most significant changes:

### Refactoring and Enhancements to Game Logic:
* Moved the `PlacedShip` class to its own file (`PlacedShip.cs`) and added new properties and methods, such as `MaxHealth`, `IsDestroyed`, and `DamageCell`, to better encapsulate ship-related logic.
* Overhauled the `HandleAttack` method in `GameState.cs` to include logic for tracking damage, determining if a ship is destroyed, and checking for game-over conditions. Added helper methods like `SendAttackResult`, `AreAllShipsDestroyed`, and `BroadcastGameOver` to improve code modularity.

### Asset Management Improvements:
* Updated asset paths in `DraggingShip.cs` to use relative paths (`Assets/icons8-rotate-48.png` and `Assets/icons8-tick-60.png`) for better portability and consistency.
* Added a new asset (`icons8-tick-60.png`) to the project file (`WaterWizard.Client.csproj`) to ensure it is included in the build output.

### Code Cleanup:
* Removed the old `HandleAttack` implementation and unused code in `GameState.cs` to eliminate redundancy and improve maintainability.
* Deleted the inline `PlacedShip` class from `GameState.cs` after moving it to a dedicated file.… tracking


Diff:
diff --git a/src/WaterWizard.Client/WaterWizard.Client.csproj b/src/WaterWizard.Client/WaterWizard.Client.csproj
index e3c9a57..37b1ac2 100644
--- a/src/WaterWizard.Client/WaterWizard.Client.csproj
+++ b/src/WaterWizard.Client/WaterWizard.Client.csproj
@@ -1,4 +1,4 @@
-﻿<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <ItemGroup>
     <Content Include="Assets\icons8-rotate-48.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
diff --git a/src/WaterWizard.Client/gamescreen/ships/DraggingShip.cs b/src/WaterWizard.Client/gamescreen/ships/DraggingShip.cs
index 343cf43..f6bb57f 100644
--- a/src/WaterWizard.Client/gamescreen/ships/DraggingShip.cs
+++ b/src/WaterWizard.Client/gamescreen/ships/DraggingShip.cs
@@ -277,6 +277,11 @@ private void HandleConfirm()
     /// <param name="shipSize"></param>
     private void SpawnShip(int x, int y, int shipSize)
     {
+        if (gameScreen.playerBoard == null)
+        {
+            Console.WriteLine("Warning: playerBoard is null, cannot spawn ship");
+            return;
+        }
         //gameScreen.playerBoard!.putShip(new(gameScreen, x, y, ShipType.DEFAULT, (int)DraggedShipRectangle.Width, (int)DraggedShipRectangle.Height));
         int boardX = (int)gameScreen.playerBoard.Position.X;
         int boardY = (int)gameScreen.playerBoard.Position.Y;
diff --git a/src/WaterWizard.Client/network/ClientService.cs b/src/WaterWizard.Client/network/ClientService.cs
index aba2696..0955f80 100644
--- a/src/WaterWizard.Client/network/ClientService.cs
+++ b/src/WaterWizard.Client/network/ClientService.cs
@@ -513,6 +513,23 @@ DeliveryMethod deliveryMethod
                 case "StartPlacementPhase":
                     GameStateManager.Instance.SetStateToPlacementPhase();
                     break;
+                case "GameOver":
+                    string result = reader.GetString();
+                    Console.WriteLine($"[Client] Game Over: {result}");
+                    
+                    GameStateManager.Instance.ChatLog.AddMessage($"Game Over: {result}");
+                    GameStateManager.Instance.ChatLog.AddMessage("Returning to lobby in 5 seconds...");
+                    
+                    var clientTimer = new System.Timers.Timer(5000);
+                    clientTimer.Elapsed += (s, e) =>
+                    {
+                        clientTimer.Stop();
+                        clientTimer.Dispose();
+                        GameStateManager.Instance.SetStateToLobby();
+                        Console.WriteLine("[Client] Transitioned back to lobby after game over");
+                    };
+                    clientTimer.Start();
+                    break;
                 case "ShipPosition":
                     try
                     {
diff --git a/src/WaterWizard.Client/network/NetworkManager.cs b/src/WaterWizard.Client/network/NetworkManager.cs
index 5200d10..65e7efb 100644
--- a/src/WaterWizard.Client/network/NetworkManager.cs
+++ b/src/WaterWizard.Client/network/NetworkManager.cs
@@ -14,7 +14,6 @@ public class NetworkManager
 
     public readonly List<LobbyInfo> discoveredLobbies = [];
 
-    private readonly bool isPlayerConnected = false;
     public readonly int hostPort = 7777;
 
     public int? LobbyCountdownSeconds { get; set; }
diff --git a/src/WaterWizard.Server/GameState.cs b/src/WaterWizard.Server/GameState.cs
index e157fb6..52be735 100644
--- a/src/WaterWizard.Server/GameState.cs
+++ b/src/WaterWizard.Server/GameState.cs
@@ -6,13 +6,7 @@
 
 namespace WaterWizard.Server;
 
-public class PlacedShip
-{
-    public int X { get; set; }
-    public int Y { get; set; }
-    public int Width { get; set; }
-    public int Height { get; set; }
-}
+
 
 /// <summary>
 /// Represents the true Gamestate of the Game as it is on the Server.
@@ -264,27 +258,6 @@ public void HandleShipPlacement(NetPeer peer, NetPacketReader reader)
         peer.Send(writer, DeliveryMethod.ReliableOrdered);
     }
 
-    public void HandleAttack(NetPeer attacker, NetPeer defender, int x, int y)
-    {
-        Console.WriteLine(
-            $"[Server] HandleAttack called: attacker={attacker}, defender={defender}, coords=({x},{y})"
-        );
-        var ships = GetShips(defender);
-        foreach (var ship in ships)
-        {
-            if (x >= ship.X && x < ship.X + ship.Width && y >= ship.Y && y < ship.Y + ship.Height)
-            {
-                Console.WriteLine(
-                    $"[Server] Treffer auf Schiff bei ({x},{y}) von Spieler {defender.ToString()}"
-                );
-                return;
-            }
-        }
-        Console.WriteLine(
-            $"[Server] Kein Schiff getroffen bei ({x},{y}) von Spieler {defender.ToString()}"
-        );
-    }
-
     /// <summary>
     /// Handles the Buying of Cards from a CardStack. Takes a random Card from the corresponding CardStack
     /// of the <see cref="CardType"/> given in <paramref name="reader"/>
@@ -357,4 +330,181 @@ public void HandleCardCasting(NetPeer peer, NetPacketReader reader)
             Console.WriteLine($"[Server] Casting Failed. Variant {cardVariantString} unknown");
         }
     }
+
+    /// <summary>
+    /// Handles the attack from one player to another.
+    /// Checks if the attack hits a ship and updates the game state accordingly.
+    /// If a ship is hit, it checks if the ship is destroyed and sends the result to both players.
+    /// </summary>
+    /// <param name="attacker">The player who initiated the attack</param>
+    /// <param name="defender">The player who was attacked</param>
+    /// <param name="x">The x-coordinate of the attack</param>
+    /// <param name="y">The y-coordinate of the attack</param>
+    public void HandleAttack(NetPeer attacker, NetPeer defender, int x, int y)
+    {
+        Console.WriteLine(
+            $"[Server] HandleAttack called: attacker={attacker}, defender={defender}, coords=({x},{y})"
+        );
+        
+        var ships = GetShips(defender);
+        bool hit = false;
+        PlacedShip? hitShip = null;
+        
+        foreach (var ship in ships)
+        {
+            if (x >= ship.X && x < ship.X + ship.Width && y >= ship.Y && y < ship.Y + ship.Height)
+            {
+                hit = true;
+                hitShip = ship;
+                
+                bool newDamage = ship.DamageCell(x, y);
+                
+                if (newDamage)
+                {
+                    Console.WriteLine($"[Server] New damage at ({x},{y}) on ship at ({ship.X},{ship.Y})");
+                    
+                    if (ship.IsDestroyed)
+                    {
+                        Console.WriteLine($"[Server] Ship at ({ship.X},{ship.Y}) destroyed!");
+                    }
+                }
+                else
+                {
+                    Console.WriteLine($"[Server] Cell ({x},{y}) already damaged");
+                }
+                break;
+            }
+        }
+        
+        if (!hit)
+        {
+            Console.WriteLine($"[Server] Miss at ({x},{y})");
+        }
+        
+        /// <summary>
+        /// Sends the result of the attack to both players.
+        /// /// </summary>
+        /// <param name="attacker">The player who initiated the attack</param>
+        /// <param name="defender">The player who was attacked</param>
+        /// <param name="x">The x-coordinate of the attack</param>
+        /// <param name="y">The y-coordinate of the attack</param>
+        /// <param name="hit">Whether the attack hit a ship</param>
+        /// <param name="shipDestroyed">Whether the ship was destroyed</param>
+        SendAttackResult(attacker, defender, x, y, hit, hitShip?.IsDestroyed ?? false);
+        
+        if (hit && hitShip?.IsDestroyed == true)
+        {
+            CheckGameOver();
+        }
+    }
+
+    /// <summary>
+    /// Sends the result of an attack to both players.
+    /// </summary>
+    /// <param name="attacker">The player who initiated the attack</param>
+    /// <param name="defender">The player who was attacked</param>
+    /// <param name="x">The x-coordinate of the attack</param>
+    /// <param name="y">The y-coordinate of the attack</param>
+    /// <param name="hit">Whether the attack hit a ship</param>
+    /// <param name="shipDestroyed">Whether the ship was destroyed</param>
+    private void SendAttackResult(NetPeer attacker, NetPeer defender, int x, int y, bool hit, bool shipDestroyed)
+    {
+        var writer = new NetDataWriter();
+        writer.Put("AttackResult");
+        writer.Put(x);
+        writer.Put(y);
+        writer.Put(hit);
+        writer.Put(shipDestroyed);
+        
+        attacker.Send(writer, DeliveryMethod.ReliableOrdered);
+        defender.Send(writer, DeliveryMethod.ReliableOrdered);
+        
+        Console.WriteLine($"[Server] Attack result sent: hit={hit}, destroyed={shipDestroyed}");
+    }
+
+    /// <summary>
+    /// Checks if all ships of a player are destroyed.
+    /// </summary>
+    /// <param name="player">The player to check</param>
+    public bool AreAllShipsDestroyed(NetPeer player)
+    {
+        var ships = GetShips(player);
+        return ships.Count > 0 && ships.All(ship => ship.IsDestroyed);
+    }
+
+    /// <summary>
+    /// Checks if the game is over by checking if any player's ships are all destroyed.
+    /// If so, it broadcasts the game over message to all players.
+    /// </summary>
+    public void CheckGameOver()
+    {
+        foreach (var player in players)
+        {
+            if (player != null && AreAllShipsDestroyed(player))
+            {
+                var winner = players.FirstOrDefault(p => p != player);
+                if (winner != null)
+                {
+                    BroadcastGameOver(winner, player);
+                }
+            }
+        }
+    }
+
+    /// <summary>
+    /// Broadcasts the game over message to all players.
+    /// </summary>
+    /// <param name="winner">The winning player</param>
+    /// <param name="loser">The losing player</param>
+    private void BroadcastGameOver(NetPeer winner, NetPeer loser)
+    {
+        var writer = new NetDataWriter();
+        writer.Put("GameOver");
+        writer.Put("Winner"); 
+        
+        foreach (var peer in server.ConnectedPeerList)
+        {
+            peer.Send(writer, DeliveryMethod.ReliableOrdered);
+        }
+        
+        Console.WriteLine($"[Server] Game Over! Winner: {winner}, Loser: {loser}");
+        
+        var gameOverTimer = new Timer(_ =>
+        {
+            Console.WriteLine("[Server] Transitioning back to lobby after game over...");
+            manager.ChangeState(new LobbyState(server));
+            
+            foreach (var playerKey in Program.ConnectedPlayers.Keys.ToList())
+            {
+                Program.ConnectedPlayers[playerKey] = false;
+            }
+            Program.PlacementReadyPlayers.Clear();
+            
+            playerShips.Clear();
+            Console.WriteLine("[Server] Ship placements cleared for next game.");
+            
+            var playerListWriter = new NetDataWriter();
+            playerListWriter.Put("PlayerList");
+            playerListWriter.Put(Program.ConnectedPlayers.Count);
+            foreach (var kvp in Program.ConnectedPlayers)
+            {
+                string playerName = Program.PlayerNames.GetValueOrDefault(kvp.Key, "Unknown");
+                playerListWriter.Put(kvp.Key);
+                playerListWriter.Put(playerName);
+                playerListWriter.Put(kvp.Value);
+            }
+            
+            foreach (var peer in server.ConnectedPeerList)
+            {
+                peer.Send(playerListWriter, DeliveryMethod.ReliableOrdered);
+            }
+            
+            Console.WriteLine("[Server] Players reset to not ready, returned to lobby.");
+            
+            if (_ is Timer timer)
+            {
+                timer.Dispose();
+            }
+        }, null, 5000, Timeout.Infinite); 
+    }
 }
diff --git a/src/WaterWizard.Server/PlacedShip.cs b/src/WaterWizard.Server/PlacedShip.cs
new file mode 100644
index 0000000..c7fa584
--- /dev/null
+++ b/src/WaterWizard.Server/PlacedShip.cs
@@ -0,0 +1,23 @@
+namespace WaterWizard.Server;
+
+/// <summary>
+/// Represents a ship that has been placed on the game board.
+/// </summary>
+public class PlacedShip
+{
+    public int X { get; set; }
+    public int Y { get; set; }
+    public int Width { get; set; }
+    public int Height { get; set; }
+
+    public int MaxHealth => Width * Height; // Total cells
+    public HashSet<(int X, int Y)> DamagedCells { get; set; } = new();
+    public bool IsDestroyed => DamagedCells.Count >= MaxHealth;
+
+    public bool IsCellDamaged(int cellX, int cellY) => DamagedCells.Contains((cellX, cellY));
+
+    public bool DamageCell(int cellX, int cellY)
+    {
+        return DamagedCells.Add((cellX, cellY));
+    }
+}
\ No newline at end of file
diff --git a/src/WaterWizard.Server/Program.cs b/src/WaterWizard.Server/Program.cs
index 975dd3a..d0e48ac 100644
--- a/src/WaterWizard.Server/Program.cs
+++ b/src/WaterWizard.Server/Program.cs
@@ -8,8 +8,8 @@ namespace WaterWizard.Server;
 static class Program
 {
     public static readonly Dictionary<string, bool> ConnectedPlayers = [];
-    private static readonly Dictionary<string, bool> PlacementReadyPlayers = [];
-    private static readonly Dictionary<string, string> PlayerNames = [];
+    public static readonly Dictionary<string, bool> PlacementReadyPlayers = [];
+    public static readonly Dictionary<string, string> PlayerNames = [];
     private static GameSessionTimer? _gameSessionTimer;
 
     private static void Log(string message)


Reviews:
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-25T11:17:15Z
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-25T21:57:35Z
user: login: justinjd00
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-26T14:12:52Z

Review comments:
diff_hunk: @@ -357,4 +330,143 @@
             Console.WriteLine($"[Server] Casting Failed. Variant {cardVariantString} unknown");
         }
     }
+
+    /// <summary>
+    /// Handles the attack from one player to another.
+    /// Checks if the attack hits a ship and updates the game state accordingly.
+    /// If a ship is hit, it checks if the ship is destroyed and sends the result to both players.
+    /// </summary>
+    /// <param name="attacker">The player who initiated the attack</param>
+    /// <param name="defender">The player who was attacked</param>
+    /// <param name="x">The x-coordinate of the attack</param>
+    /// <param name="y">The y-coordinate of the attack</param>
+    public void HandleAttack(NetPeer attacker, NetPeer defender, int x, int y)
+    {
+        Console.WriteLine(
+            $"[Server] HandleAttack called: attacker={attacker}, defender={defender}, coords=({x},{y})"
+        );
+        
+        var ships = GetShips(defender);
+        bool hit = false;
+        PlacedShip? hitShip = null;
+        
+        foreach (var ship in ships)
+        {
+            if (x >= ship.X && x < ship.X + ship.Width && y >= ship.Y && y < ship.Y + ship.Height)
+            {
+                hit = true;
+                hitShip = ship;
+                
+                bool newDamage = ship.DamageCell(x, y);
+                
+                if (newDamage)
+                {
+                    Console.WriteLine($"[Server] New damage at ({x},{y}) on ship at ({ship.X},{ship.Y})");
+                    
+                    if (ship.IsDestroyed)
+                    {
+                        Console.WriteLine($"[Server] Ship at ({ship.X},{ship.Y}) destroyed!");
+                    }
+                }
+                else
+                {
+                    Console.WriteLine($"[Server] Cell ({x},{y}) already damaged");
+                }
+                break;
+            }
+        }
path: src/WaterWizard.Server/GameState.cs
user: login: github-advanced-security[bot]
body: ## Missed opportunity to use Where

This foreach loop [implicitly filters its target sequence](1) - consider filtering the sequence explicitly using '.Where(...)'.

[Show more details](https://github.com/maxk2807/WaterWizards/security/code-scanning/842)
created_at: 2025-05-25T11:17:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -357,4 +330,143 @@
             Console.WriteLine($"[Server] Casting Failed. Variant {cardVariantString} unknown");
         }
     }
+
+    /// <summary>
+    /// Handles the attack from one player to another.
+    /// Checks if the attack hits a ship and updates the game state accordingly.
+    /// If a ship is hit, it checks if the ship is destroyed and sends the result to both players.
+    /// </summary>
+    /// <param name="attacker">The player who initiated the attack</param>
+    /// <param name="defender">The player who was attacked</param>
+    /// <param name="x">The x-coordinate of the attack</param>
+    /// <param name="y">The y-coordinate of the attack</param>
+    public void HandleAttack(NetPeer attacker, NetPeer defender, int x, int y)
+    {
+        Console.WriteLine(
+            $"[Server] HandleAttack called: attacker={attacker}, defender={defender}, coords=({x},{y})"
+        );
+        
+        var ships = GetShips(defender);
+        bool hit = false;
+        PlacedShip? hitShip = null;
+        
+        foreach (var ship in ships)
+        {
+            if (x >= ship.X && x < ship.X + ship.Width && y >= ship.Y && y < ship.Y + ship.Height)
+            {
+                hit = true;
+                hitShip = ship;
+                
+                bool newDamage = ship.DamageCell(x, y);
+                
+                if (newDamage)
+                {
+                    Console.WriteLine($"[Server] New damage at ({x},{y}) on ship at ({ship.X},{ship.Y})");
+                    
+                    if (ship.IsDestroyed)
+                    {
+                        Console.WriteLine($"[Server] Ship at ({ship.X},{ship.Y}) destroyed!");
+                    }
+                }
+                else
+                {
+                    Console.WriteLine($"[Server] Cell ({x},{y}) already damaged");
+                }
+                break;
+            }
+        }
+        
+        if (!hit)
+        {
+            Console.WriteLine($"[Server] Miss at ({x},{y})");
+        }
+        
+        /// <summary>
+        /// Sends the result of the attack to both players.
+        /// /// </summary>
+        /// <param name="attacker">The player who initiated the attack</param>
+        /// <param name="defender">The player who was attacked</param>
+        /// <param name="x">The x-coordinate of the attack</param>
+        /// <param name="y">The y-coordinate of the attack</param>
+        /// <param name="hit">Whether the attack hit a ship</param>
+        /// <param name="shipDestroyed">Whether the ship was destroyed</param>
+        SendAttackResult(attacker, defender, x, y, hit, hitShip?.IsDestroyed ?? false);
+        
+        if (hit && hitShip?.IsDestroyed == true)
+        {
+            CheckGameOver();
+        }
+    }
+
+    /// <summary>
+    /// Sends the result of an attack to both players.
+    /// </summary>
+    /// <param name="attacker">The player who initiated the attack</param>
+    /// <param name="defender">The player who was attacked</param>
+    /// <param name="x">The x-coordinate of the attack</param>
+    /// <param name="y">The y-coordinate of the attack</param>
+    /// <param name="hit">Whether the attack hit a ship</param>
+    /// <param name="shipDestroyed">Whether the ship was destroyed</param>
+    private void SendAttackResult(NetPeer attacker, NetPeer defender, int x, int y, bool hit, bool shipDestroyed)
+    {
+        var writer = new NetDataWriter();
+        writer.Put("AttackResult");
+        writer.Put(x);
+        writer.Put(y);
+        writer.Put(hit);
+        writer.Put(shipDestroyed);
+        
+        attacker.Send(writer, DeliveryMethod.ReliableOrdered);
+        defender.Send(writer, DeliveryMethod.ReliableOrdered);
+        
+        Console.WriteLine($"[Server] Attack result sent: hit={hit}, destroyed={shipDestroyed}");
+    }
+
+    /// <summary>
+    /// Checks if all ships of a player are destroyed.
+    /// </summary>
+    /// <param name="player">The player to check</param>
+    public bool AreAllShipsDestroyed(NetPeer player)
+    {
+        var ships = GetShips(player);
+        return ships.Count > 0 && ships.All(ship => ship.IsDestroyed);
+    }
+
+    /// <summary>
+    /// Checks if the game is over by checking if any player's ships are all destroyed.
+    /// If so, it broadcasts the game over message to all players.
+    /// </summary>
+    public void CheckGameOver()
+    {
+        foreach (var player in players)
+        {
+            if (player != null && AreAllShipsDestroyed(player))
+            {
+                var winner = players.FirstOrDefault(p => p != player);
+                if (winner != null)
+                {
+                    BroadcastGameOver(winner, player);
+                }
+            }
+        }
path: src/WaterWizard.Server/GameState.cs
user: login: github-advanced-security[bot]
body: ## Missed opportunity to use Where

This foreach loop [implicitly filters its target sequence](1) - consider filtering the sequence explicitly using '.Where(...)'.

[Show more details](https://github.com/maxk2807/WaterWizards/security/code-scanning/843)
created_at: 2025-05-25T11:17:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -357,4 +330,176 @@
             Console.WriteLine($"[Server] Casting Failed. Variant {cardVariantString} unknown");
         }
     }
+
+    /// <summary>
+    /// Handles the attack from one player to another.
+    /// Checks if the attack hits a ship and updates the game state accordingly.
+    /// If a ship is hit, it checks if the ship is destroyed and sends the result to both players.
+    /// </summary>
+    /// <param name="attacker">The player who initiated the attack</param>
+    /// <param name="defender">The player who was attacked</param>
+    /// <param name="x">The x-coordinate of the attack</param>
+    /// <param name="y">The y-coordinate of the attack</param>
+    public void HandleAttack(NetPeer attacker, NetPeer defender, int x, int y)
+    {
+        Console.WriteLine(
+            $"[Server] HandleAttack called: attacker={attacker}, defender={defender}, coords=({x},{y})"
+        );
+        
+        var ships = GetShips(defender);
+        bool hit = false;
+        PlacedShip? hitShip = null;
+        
+        foreach (var ship in ships)
+        {
+            if (x >= ship.X && x < ship.X + ship.Width && y >= ship.Y && y < ship.Y + ship.Height)
+            {
+                hit = true;
+                hitShip = ship;
+                
+                bool newDamage = ship.DamageCell(x, y);
+                
+                if (newDamage)
+                {
+                    Console.WriteLine($"[Server] New damage at ({x},{y}) on ship at ({ship.X},{ship.Y})");
+                    
+                    if (ship.IsDestroyed)
+                    {
+                        Console.WriteLine($"[Server] Ship at ({ship.X},{ship.Y}) destroyed!");
+                    }
+                }
+                else
+                {
+                    Console.WriteLine($"[Server] Cell ({x},{y}) already damaged");
+                }
+                break;
+            }
+        }
+        
+        if (!hit)
+        {
+            Console.WriteLine($"[Server] Miss at ({x},{y})");
+        }
+        
+        /// <summary>
+        /// Sends the result of the attack to both players.
+        /// /// </summary>
+        /// <param name="attacker">The player who initiated the attack</param>
+        /// <param name="defender">The player who was attacked</param>
+        /// <param name="x">The x-coordinate of the attack</param>
+        /// <param name="y">The y-coordinate of the attack</param>
+        /// <param name="hit">Whether the attack hit a ship</param>
+        /// <param name="shipDestroyed">Whether the ship was destroyed</param>
+        SendAttackResult(attacker, defender, x, y, hit, hitShip?.IsDestroyed ?? false);
+        
+        if (hit && hitShip?.IsDestroyed == true)
+        {
+            CheckGameOver();
+        }
+    }
+
+    /// <summary>
+    /// Sends the result of an attack to both players.
+    /// </summary>
+    /// <param name="attacker">The player who initiated the attack</param>
+    /// <param name="defender">The player who was attacked</param>
+    /// <param name="x">The x-coordinate of the attack</param>
+    /// <param name="y">The y-coordinate of the attack</param>
+    /// <param name="hit">Whether the attack hit a ship</param>
+    /// <param name="shipDestroyed">Whether the ship was destroyed</param>
+    private void SendAttackResult(NetPeer attacker, NetPeer defender, int x, int y, bool hit, bool shipDestroyed)
+    {
+        var writer = new NetDataWriter();
+        writer.Put("AttackResult");
+        writer.Put(x);
+        writer.Put(y);
+        writer.Put(hit);
+        writer.Put(shipDestroyed);
+        
+        attacker.Send(writer, DeliveryMethod.ReliableOrdered);
+        defender.Send(writer, DeliveryMethod.ReliableOrdered);
+        
+        Console.WriteLine($"[Server] Attack result sent: hit={hit}, destroyed={shipDestroyed}");
+    }
+
+    /// <summary>
+    /// Checks if all ships of a player are destroyed.
+    /// </summary>
+    /// <param name="player">The player to check</param>
+    public bool AreAllShipsDestroyed(NetPeer player)
+    {
+        var ships = GetShips(player);
+        return ships.Count > 0 && ships.All(ship => ship.IsDestroyed);
+    }
+
+    /// <summary>
+    /// Checks if the game is over by checking if any player's ships are all destroyed.
+    /// If so, it broadcasts the game over message to all players.
+    /// </summary>
+    public void CheckGameOver()
+    {
+        foreach (var player in players)
+        {
+            if (player != null && AreAllShipsDestroyed(player))
+            {
+                var winner = players.FirstOrDefault(p => p != player);
+                if (winner != null)
+                {
+                    BroadcastGameOver(winner, player);
+                }
+            }
+        }
+    }
+
+    /// <summary>
+    /// Broadcasts the game over message to all players.
+    /// </summary>
+    /// <param name="winner">The winning player</param>
+    /// <param name="loser">The losing player</param>
+    private void BroadcastGameOver(NetPeer winner, NetPeer loser)
+    {
+        var writer = new NetDataWriter();
+        writer.Put("GameOver");
+        writer.Put("Winner"); 
+        
+        foreach (var peer in server.ConnectedPeerList)
+        {
+            peer.Send(writer, DeliveryMethod.ReliableOrdered);
+        }
+        
+        Console.WriteLine($"[Server] Game Over! Winner: {winner}, Loser: {loser}");
+        
+        var gameOverTimer = new Timer(_ =>
+        {
+            Console.WriteLine("[Server] Transitioning back to lobby after game over...");
+            manager.ChangeState(new LobbyState(server));
+            
+            foreach (var playerKey in Program.ConnectedPlayers.Keys.ToList())
+            {
+                Program.ConnectedPlayers[playerKey] = false;
+            }
+            Program.PlacementReadyPlayers.Clear();
+            
+            playerShips.Clear();
+            Console.WriteLine("[Server] Ship placements cleared for next game.");
+            
+            var playerListWriter = new NetDataWriter();
+            playerListWriter.Put("PlayerList");
+            playerListWriter.Put(Program.ConnectedPlayers.Count);
+            foreach (var kvp in Program.ConnectedPlayers)
+            {
+                string playerName = Program.PlayerNames.GetValueOrDefault(kvp.Key, "Unknown");
+                playerListWriter.Put(kvp.Key);
+                playerListWriter.Put(playerName);
+                playerListWriter.Put(kvp.Value);
+            }
+            
+            foreach (var peer in server.ConnectedPeerList)
+            {
+                peer.Send(playerListWriter, DeliveryMethod.ReliableOrdered);
+            }
+            
+            Console.WriteLine("[Server] Players reset to not ready, returned to lobby.");
+        }, null, 5000, Timeout.Infinite); 
path: src/WaterWizard.Server/GameState.cs
user: login: github-advanced-security[bot]
body: ## Useless assignment to local variable

This assignment to [gameOverTimer](1) is useless, since its value is never read.

[Show more details](https://github.com/maxk2807/WaterWizards/security/code-scanning/845)
created_at: 2025-05-25T21:57:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -357,4 +330,176 @@
             Console.WriteLine($"[Server] Casting Failed. Variant {cardVariantString} unknown");
         }
     }
+
+    /// <summary>
+    /// Handles the attack from one player to another.
+    /// Checks if the attack hits a ship and updates the game state accordingly.
+    /// If a ship is hit, it checks if the ship is destroyed and sends the result to both players.
+    /// </summary>
+    /// <param name="attacker">The player who initiated the attack</param>
+    /// <param name="defender">The player who was attacked</param>
+    /// <param name="x">The x-coordinate of the attack</param>
+    /// <param name="y">The y-coordinate of the attack</param>
+    public void HandleAttack(NetPeer attacker, NetPeer defender, int x, int y)
+    {
+        Console.WriteLine(
+            $"[Server] HandleAttack called: attacker={attacker}, defender={defender}, coords=({x},{y})"
+        );
+        
+        var ships = GetShips(defender);
+        bool hit = false;
+        PlacedShip? hitShip = null;
+        
+        foreach (var ship in ships)
+        {
+            if (x >= ship.X && x < ship.X + ship.Width && y >= ship.Y && y < ship.Y + ship.Height)
+            {
+                hit = true;
+                hitShip = ship;
+                
+                bool newDamage = ship.DamageCell(x, y);
+                
+                if (newDamage)
+                {
+                    Console.WriteLine($"[Server] New damage at ({x},{y}) on ship at ({ship.X},{ship.Y})");
+                    
+                    if (ship.IsDestroyed)
+                    {
+                        Console.WriteLine($"[Server] Ship at ({ship.X},{ship.Y}) destroyed!");
+                    }
+                }
+                else
+                {
+                    Console.WriteLine($"[Server] Cell ({x},{y}) already damaged");
+                }
+                break;
+            }
+        }
+        
+        if (!hit)
+        {
+            Console.WriteLine($"[Server] Miss at ({x},{y})");
+        }
+        
+        /// <summary>
+        /// Sends the result of the attack to both players.
+        /// /// </summary>
+        /// <param name="attacker">The player who initiated the attack</param>
+        /// <param name="defender">The player who was attacked</param>
+        /// <param name="x">The x-coordinate of the attack</param>
+        /// <param name="y">The y-coordinate of the attack</param>
+        /// <param name="hit">Whether the attack hit a ship</param>
+        /// <param name="shipDestroyed">Whether the ship was destroyed</param>
+        SendAttackResult(attacker, defender, x, y, hit, hitShip?.IsDestroyed ?? false);
+        
+        if (hit && hitShip?.IsDestroyed == true)
+        {
+            CheckGameOver();
+        }
+    }
+
+    /// <summary>
+    /// Sends the result of an attack to both players.
+    /// </summary>
+    /// <param name="attacker">The player who initiated the attack</param>
+    /// <param name="defender">The player who was attacked</param>
+    /// <param name="x">The x-coordinate of the attack</param>
+    /// <param name="y">The y-coordinate of the attack</param>
+    /// <param name="hit">Whether the attack hit a ship</param>
+    /// <param name="shipDestroyed">Whether the ship was destroyed</param>
+    private void SendAttackResult(NetPeer attacker, NetPeer defender, int x, int y, bool hit, bool shipDestroyed)
+    {
+        var writer = new NetDataWriter();
+        writer.Put("AttackResult");
+        writer.Put(x);
+        writer.Put(y);
+        writer.Put(hit);
+        writer.Put(shipDestroyed);
+        
+        attacker.Send(writer, DeliveryMethod.ReliableOrdered);
+        defender.Send(writer, DeliveryMethod.ReliableOrdered);
+        
+        Console.WriteLine($"[Server] Attack result sent: hit={hit}, destroyed={shipDestroyed}");
+    }
+
+    /// <summary>
+    /// Checks if all ships of a player are destroyed.
+    /// </summary>
+    /// <param name="player">The player to check</param>
+    public bool AreAllShipsDestroyed(NetPeer player)
+    {
+        var ships = GetShips(player);
+        return ships.Count > 0 && ships.All(ship => ship.IsDestroyed);
+    }
+
+    /// <summary>
+    /// Checks if the game is over by checking if any player's ships are all destroyed.
+    /// If so, it broadcasts the game over message to all players.
+    /// </summary>
+    public void CheckGameOver()
+    {
+        foreach (var player in players)
+        {
+            if (player != null && AreAllShipsDestroyed(player))
+            {
+                var winner = players.FirstOrDefault(p => p != player);
+                if (winner != null)
+                {
+                    BroadcastGameOver(winner, player);
+                }
+            }
+        }
+    }
+
+    /// <summary>
+    /// Broadcasts the game over message to all players.
+    /// </summary>
+    /// <param name="winner">The winning player</param>
+    /// <param name="loser">The losing player</param>
+    private void BroadcastGameOver(NetPeer winner, NetPeer loser)
+    {
+        var writer = new NetDataWriter();
+        writer.Put("GameOver");
+        writer.Put("Winner"); 
+        
+        foreach (var peer in server.ConnectedPeerList)
+        {
+            peer.Send(writer, DeliveryMethod.ReliableOrdered);
+        }
+        
+        Console.WriteLine($"[Server] Game Over! Winner: {winner}, Loser: {loser}");
+        
+        var gameOverTimer = new Timer(_ =>
+        {
+            Console.WriteLine("[Server] Transitioning back to lobby after game over...");
+            manager.ChangeState(new LobbyState(server));
+            
+            foreach (var playerKey in Program.ConnectedPlayers.Keys.ToList())
+            {
+                Program.ConnectedPlayers[playerKey] = false;
+            }
+            Program.PlacementReadyPlayers.Clear();
+            
+            playerShips.Clear();
+            Console.WriteLine("[Server] Ship placements cleared for next game.");
+            
+            var playerListWriter = new NetDataWriter();
+            playerListWriter.Put("PlayerList");
+            playerListWriter.Put(Program.ConnectedPlayers.Count);
+            foreach (var kvp in Program.ConnectedPlayers)
+            {
+                string playerName = Program.PlayerNames.GetValueOrDefault(kvp.Key, "Unknown");
+                playerListWriter.Put(kvp.Key);
+                playerListWriter.Put(playerName);
+                playerListWriter.Put(kvp.Value);
+            }
+            
+            foreach (var peer in server.ConnectedPeerList)
+            {
+                peer.Send(playerListWriter, DeliveryMethod.ReliableOrdered);
+            }
+            
+            Console.WriteLine("[Server] Players reset to not ready, returned to lobby.");
+        }, null, 5000, Timeout.Infinite); 
path: src/WaterWizard.Server/GameState.cs
user: login: github-advanced-security[bot]
body: ## Missing Dispose call on local IDisposable

Disposable 'Timer' is created but not disposed.

[Show more details](https://github.com/maxk2807/WaterWizards/security/code-scanning/844)
created_at: 2025-05-25T21:57:35Z
author_association: CONTRIBUTOR

PR comments:
user: login: Erickk0
created_at: 2025-05-25T21:55:54Z
body: Bitte die Funktionalität testen und dann erst durchwinken danke 
author_association: COLLABORATOR
user: login: justinjd00
created_at: 2025-05-26T13:46:51Z
body: Die Icons/assets für das shipplacement sind rausgefallen, schau nochmal wieso die nicht angezeigt werden.
Siehe screenshot
<img width="250" alt="image" src="https://github.com/user-attachments/assets/918877ce-42ac-4f05-af5f-49a035d295ca" />

author_association: COLLABORATOR
user: login: Erickk0
created_at: 2025-05-26T13:51:42Z
body: Bei mir werden die angezeigt?
Liegt sehr wahrscheinlich an der .csproj
author_association: COLLABORATOR
user: login: justinjd00
created_at: 2025-05-26T13:53:49Z
body: lol der hat die aus dem branch nicht gepulled?
// WaterWizard.Client.csproj
```
<Project Sdk="Microsoft.NET.Sdk">
  <ItemGroup>
    <Content Include="Assets\icons8-rotate-48.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="Assets\icons8-tick-60.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <ProjectReference Include="..\WaterWizard.Shared\WaterWizard.Shared.csproj" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="LiteNetLib" Version="1.3.1" />
    <PackageReference Include="Raylib-cs" Version="7.0.1" />
  </ItemGroup>
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

```
author_association: COLLABORATOR
user: login: Erickk0
created_at: 2025-05-26T13:56:32Z
body: wtf

author_association: COLLABORATOR
user: login: justinjd00
created_at: 2025-05-26T14:04:52Z
body: mit dieser csproj funktioniert es 👍 
```

<Project Sdk="Microsoft.NET.Sdk">
  <ItemGroup>
    <Content Include="Assets\icons8-rotate-48.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <ProjectReference Include="..\WaterWizard.Shared\WaterWizard.Shared.csproj" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="LiteNetLib" Version="1.3.1" />
    <PackageReference Include="Raylib-cs" Version="7.0.1" />
  </ItemGroup>
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

```
author_association: COLLABORATOR
user: login: Erickk0
created_at: 2025-05-26T14:07:52Z
body: perfekt 
author_association: COLLABORATOR