From 905e13090fc6bdb16f36a879218a70f4088a457e Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Mon, 3 Feb 2025 18:40:58 +0100 Subject: [PATCH 01/10] Add comment to calcState function --- src/game/Unit.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/game/Unit.hpp b/src/game/Unit.hpp index 544c3d9..4814e7a 100644 --- a/src/game/Unit.hpp +++ b/src/game/Unit.hpp @@ -105,9 +105,14 @@ class Unit This function needs to be able to determine the possible movement-paths the unit can take MUST take into consideration that different units behave differently on certain terrain MUST show all movements possible + Requires dijkstras algorithm and the required graphs */ void calculateMovement(); - + + /* + This function calculates the difference between current position and desired position. + It updates the unit_state accordingly, so that the units face the correct direction. + */ void calcState(int posX, int posY); /* -- GitLab From d8fb0a0a2684803834de313001f42f5b2cb1df6c Mon Sep 17 00:00:00 2001 From: Lorenz <lorenz-martin.diel@informatik.hs-fulda.de> Date: Tue, 4 Feb 2025 19:47:51 +0100 Subject: [PATCH 02/10] all Units get their attributes form the xml file, First parts of wavefront --- config.xml | 4 ++-- src/game/Config.cpp | 47 ++++++++++++++++++--------------------- src/game/Config.hpp | 4 ++-- src/game/Level.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++- src/game/Level.hpp | 6 +++++ src/game/Tile.cpp | 4 ++++ src/game/Tile.hpp | 1 + src/game/Unit.cpp | 41 +++++++++++++++++++++++++++------- src/game/Unit.hpp | 20 ++++++++++++++--- src/game/ui/Menu.cpp | 5 ++++- 10 files changed, 142 insertions(+), 43 deletions(-) diff --git a/config.xml b/config.xml index d240753..5b5b962 100644 --- a/config.xml +++ b/config.xml @@ -397,8 +397,8 @@ <Damage unitId="battle_helicopter" value="105"/> <Damage unitId="fighter" value="85"/> <Damage unitId="bomber" value="100"/> - <Damage unitId="Cruiser" value="25" /> - <Damage unitId="Battleship" value="5" /> + <Damage unitId="cruiser" value="25" /> + <Damage unitId="battleship" value="5" /> </DamageTable> </PrimaryWeapon> <SecondaryWeapon name="Anti-Air Gun"> diff --git a/src/game/Config.cpp b/src/game/Config.cpp index 823a738..2a01848 100644 --- a/src/game/Config.cpp +++ b/src/game/Config.cpp @@ -3,6 +3,7 @@ #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/xml_parser.hpp> #include <iostream> +#include <optional> #include <stdexcept> #include <string> #include <unordered_map> @@ -206,7 +207,7 @@ std::string Config::getUnitPrimaryWeapon(UnitId id) const { return it->second; } - throw std::runtime_error("Primary weapon for unit ID not found"); + return ""; } std::string Config::getUnitSecondaryWeapon(UnitId id) const @@ -216,36 +217,30 @@ std::string Config::getUnitSecondaryWeapon(UnitId id) const { return it->second; } - throw std::runtime_error("Secondary weapon for unit ID not found"); + return ""; } -int Config::getUnitPrimaryWeaponDamage(UnitId attackerid, UnitId defenderid) const -{ - auto it = m_primaryWeaponDamage.find(attackerid); - if (it != m_primaryWeaponDamage.end()) - { - auto damageIt = it->second.find(defenderid); - if (damageIt != it->second.end()) - { - return damageIt->second; +std::optional<int> Config::getUnitPrimaryWeaponDamage(UnitId attackerId, UnitId targetId) const { + auto attackerMapIt = m_primaryWeaponDamage.find(attackerId); + if (attackerMapIt != m_primaryWeaponDamage.end()) { + auto damageIt = attackerMapIt->second.find(targetId); + if (damageIt != attackerMapIt->second.end()) { + return damageIt->second; + } } + // Kein spezifischer Schaden vorhanden + return std::nullopt; } - throw std::runtime_error( - "Primary weapon damage not found for given attacker/defender combination"); -} -int Config::getUnitSecondaryWeaponDamage(UnitId attackerid, UnitId defenderid) const -{ - auto it = m_secondaryWeaponDamage.find(attackerid); - if (it != m_secondaryWeaponDamage.end()) - { - auto damageIt = it->second.find(defenderid); - if (damageIt != it->second.end()) - { - return damageIt->second; +std::optional<int> Config::getUnitSecondaryWeaponDamage(UnitId attackerId, UnitId targetId) const { + auto attackerMapIt = m_secondaryWeaponDamage.find(attackerId); + if (attackerMapIt != m_secondaryWeaponDamage.end()) { + auto damageIt = attackerMapIt->second.find(targetId); + if (damageIt != attackerMapIt->second.end()) { + return damageIt->second; + } } + // Kein spezifischer Schaden vorhanden + return std::nullopt; } - throw std::runtime_error( - "Secondary weapon damage not found for given attacker/defender combination"); -} } // namespace advanced_wars \ No newline at end of file diff --git a/src/game/Config.hpp b/src/game/Config.hpp index 2b03c1b..d3f43fd 100644 --- a/src/game/Config.hpp +++ b/src/game/Config.hpp @@ -53,11 +53,11 @@ class Config /** @brief Retrieves the damage value of a unit's primary weapon against a target unit type. */ - int getUnitPrimaryWeaponDamage(UnitId attackerid, UnitId defenderid) const; + std::optional<int> getUnitPrimaryWeaponDamage(UnitId attackerid, UnitId defenderid) const; /** @brief Retrieves the damage value of a unit's secondary weapon against a target unit * type. */ - int getUnitSecondaryWeaponDamage(UnitId attackerid, UnitId defenderid) const; + std::optional<int> getUnitSecondaryWeaponDamage(UnitId attackerid, UnitId defenderid) const; /** @brief Retrieves the movement type of a given unit type. */ MovementType getUnitMovementType(UnitId id) const; diff --git a/src/game/Level.cpp b/src/game/Level.cpp index 67e35e5..b4458af 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -4,9 +4,11 @@ #include "Engine.hpp" #include "Spritesheet.hpp" #include "Unit.hpp" +#include "Config.hpp" #include "highfive/H5File.hpp" #include "ui/Contextmenu.hpp" #include "ui/Pausemenu.hpp" +#include <queue> #include <SDL.h> #include <algorithm> #include <boost/property_tree/ptree.hpp> @@ -235,7 +237,7 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) } else { - + calculateMovementRange(m_units.at(m_selectedUnit)); m_units.at(m_selectedUnit).updatePosition(tileX, tileY); } } @@ -277,6 +279,55 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) } } +std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) { + std::vector<std::pair<int, int>> reachableTiles; + std::queue<std::tuple<int, int, int>> wavefrontQueue; // x, y, remainingMovement + + wavefrontQueue.push(std::make_tuple(unit.m_x, unit.m_y, unit.m_movementPoints)); + std::unordered_map<int, std::unordered_map<int, bool>> visited; + + while (!wavefrontQueue.empty()) { + auto [x, y, remainingMovement] = wavefrontQueue.front(); + wavefrontQueue.pop(); + + if (visited[x][y]) continue; + visited[x][y] = true; + + reachableTiles.emplace_back(x, y); + + static const std::vector<std::pair<int, int>> directions = { + { 1, 0}, + {-1, 0}, + { 0, 1}, + { 0, -1} + }; + + for (const auto& [dx, dy] : directions) { + int nx = x + dx; + int ny = y + dy; + + if (nx < 0 || nx >= m_width || ny < 0 || ny >= m_height) continue; // Boundary check + + int cost = getMoveCost(m_tiles[ny * m_width + nx].getType(), unit.m_movementType); + if (cost >= 0 && remainingMovement >= cost) { + wavefrontQueue.push(std::make_tuple(nx, ny, remainingMovement - cost)); + } + } + } + + std::cout << "Reachable Tiles: " << std::endl; + for (const auto& tile : reachableTiles) { + std::cout << "(" << tile.first << ", " << tile.second << ")" << std::endl; + } + + + return reachableTiles; +} +int Level::getMoveCost(TileId type, MovementType movementType) { + // Implementieren Sie die Logik zur Berechnung der Bewegungskosten hier + return 1; // Beispielrückgabe, passen Sie dies nach Bedarf an +} + void Level::render(Engine& engine) { diff --git a/src/game/Level.hpp b/src/game/Level.hpp index 49c076f..53aa865 100644 --- a/src/game/Level.hpp +++ b/src/game/Level.hpp @@ -15,6 +15,8 @@ namespace advanced_wars { + + /** * @brief The main window of the game */ @@ -43,6 +45,10 @@ class Level : public Scene Effect removeEffect(int id); + std::vector<std::pair<int, int>> calculateMovementRange(Unit& unit); + + int getMoveCost(TileId type, MovementType movementType); + private: std::string m_name; int m_width; diff --git a/src/game/Tile.cpp b/src/game/Tile.cpp index bf393db..b7e93a4 100644 --- a/src/game/Tile.cpp +++ b/src/game/Tile.cpp @@ -30,4 +30,8 @@ void Tile::render(Engine& engine, int scale) &dest, 0, NULL, SDL_FLIP_NONE); } +TileId Tile::getType() const { + return m_id; // Gibt den gespeicherten TileId zurück +} + } // namespace advanced_wars \ No newline at end of file diff --git a/src/game/Tile.hpp b/src/game/Tile.hpp index 44611fa..7a3d838 100644 --- a/src/game/Tile.hpp +++ b/src/game/Tile.hpp @@ -49,6 +49,7 @@ class Tile int m_y; void render(Engine& engine, int scale); + TileId getType() const; }; } // namespace advanced_wars \ No newline at end of file diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 657f24c..e013e97 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -1,21 +1,45 @@ #include "Unit.hpp" +#include "Config.hpp" #include <iostream> namespace advanced_wars { -Unit::Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state) +Unit::Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state, Config& config) : m_x(x), m_y(y), m_faction(faction), m_id(id), m_state(state), m_maxHealth(100) { - // das ist nur für Testzwecke - if (m_id == UnitId::INFANTERY) + // Allgemeine Einheiteneinstellungen aus Konfiguration holen + m_cost = config.getUnitCost(id); + m_movementPoints = config.getUnitMovementPoints(id); + m_ammo = config.getUnitAmmo(id); + m_minRange = config.getUnitMinRange(id); + m_maxRange = config.getUnitMaxRange(id); + m_health = m_maxHealth; + + m_movementType = config.getUnitMovementType(id); + + // Initialisieren der Primär- und Sekundärwaffe + std::unordered_map<UnitId, int> primaryDamage; + std::unordered_map<UnitId, int> secondaryDamage; + + for (int targetIt = static_cast<int>(UnitId::FIRST); targetIt <= static_cast<int>(UnitId::LAST); + ++targetIt) { - m_secondaryWeapon = Weapon( - "Machine-Gun", { - {UnitId::INFANTERY, 55} - }); + UnitId targetId = static_cast<UnitId>(targetIt); + + // Prüfen, ob ein gültiger Schadenswert vorhanden ist, und nur dann hinzufügen + if (auto damage = config.getUnitPrimaryWeaponDamage(id, targetId)) + { + primaryDamage[targetId] = *damage; + } + if (auto damage = config.getUnitSecondaryWeaponDamage(id, targetId)) + { + secondaryDamage[targetId] = *damage; + } } - m_health = m_maxHealth; + + m_primaryWeapon = Weapon(config.getUnitPrimaryWeapon(id), primaryDamage); + m_secondaryWeapon = Weapon(config.getUnitSecondaryWeapon(id), secondaryDamage); } void Unit::render(Engine& engine, int scale) @@ -83,6 +107,7 @@ void Unit::attack(Unit& enemy) { // Angenommen, primary_weapon und secondary_weapon wurden bereits korrekt // initialisiert + auto primary_weapon_damage_it = m_primaryWeapon.m_damage.find(enemy.m_id); auto secondary_weapon_damage_it = m_secondaryWeapon.m_damage.find(enemy.m_id); diff --git a/src/game/Unit.hpp b/src/game/Unit.hpp index 4814e7a..b9f645b 100644 --- a/src/game/Unit.hpp +++ b/src/game/Unit.hpp @@ -1,6 +1,7 @@ #pragma once #include "Engine.hpp" + #include "Weapon.hpp" #include <optional> #include <unordered_map> @@ -8,6 +9,8 @@ namespace advanced_wars { +class Config; + enum class UnitFaction { URED = 0, @@ -38,6 +41,9 @@ enum class UnitId CRUISER = 16, LANDER = 17, SUBMARINE = 18, + + FIRST= INFANTERY, + LAST = SUBMARINE }; enum class UnitState @@ -69,7 +75,10 @@ class Unit int m_y; int m_health; // health equals max_health at construction - Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state); + int m_movementPoints; + MovementType m_movementType; + + Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state, Config& config); ~Unit() { // Assuming that the destruktion of a unit triggers events @@ -108,7 +117,7 @@ class Unit Requires dijkstras algorithm and the required graphs */ void calculateMovement(); - + /* This function calculates the difference between current position and desired position. It updates the unit_state accordingly, so that the units face the correct direction. @@ -128,8 +137,9 @@ class Unit int m_maxHealth; // max_health required for damage_scaling int m_range; + /* int m_fuel; - int m_maxFuel; + int m_maxFuel;*/ bool m_hasMoved; bool m_hasAttacked; @@ -139,7 +149,11 @@ class Unit Weapon m_secondaryWeapon; Weapon m_primaryWeapon; + int m_cost; + int m_ammo; + int m_minRange; + int m_maxRange; }; } // namespace advanced_wars \ No newline at end of file diff --git a/src/game/ui/Menu.cpp b/src/game/ui/Menu.cpp index 392209b..c7a7c1f 100644 --- a/src/game/ui/Menu.cpp +++ b/src/game/ui/Menu.cpp @@ -1,6 +1,7 @@ #include "Menu.hpp" #include "../Building.hpp" #include "../Level.hpp" +#include "../Config.hpp" #include "../Spritesheet.hpp" #include "../Tile.hpp" #include "../Unit.hpp" @@ -126,6 +127,7 @@ void Menu::handleEvent(Engine& engine, SDL_Event& event) { std::cout << "Starting game..." << std::endl; + // Construct a level std::vector<Tile> tiles; for (int y = 0; y < 20; y++) @@ -179,6 +181,7 @@ void Menu::handleEvent(Engine& engine, SDL_Event& event) } } + Config config = Config("../config.xml"); // Units std::vector<Unit> units; @@ -188,7 +191,7 @@ void Menu::handleEvent(Engine& engine, SDL_Event& event) { units.push_back(Unit( x + 9, y + 2, UnitFaction::URED, static_cast<UnitId>(y), - static_cast<UnitState>(x))); + static_cast<UnitState>(x), config)); } } -- GitLab From f1d52b3dc4837cff30478b0da488532abedfb7fb Mon Sep 17 00:00:00 2001 From: Lorenz <lorenz-martin.diel@informatik.hs-fulda.de> Date: Tue, 4 Feb 2025 21:25:27 +0100 Subject: [PATCH 03/10] wavefront now uses different costs for combination of MoveType+TileId --- src/game/Level.cpp | 47 ++++++++++++++++++++++++++++++++++------------ src/game/Level.hpp | 45 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/src/game/Level.cpp b/src/game/Level.cpp index b4458af..da19c85 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -200,7 +200,9 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) if (m_selectedUnit > -1) { + m_reachableTiles = calculateMovementRange(m_units.at(m_selectedUnit)); m_units.at(m_selectedUnit).on_left_click(event); + m_showReachableTiles = true; } if (m_selectedBuilding > -1) @@ -214,6 +216,7 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) std::cout << "Neither building nor unit clicked!" << std::endl; m_selectedUnit = -1; m_selectedBuilding = -1; + m_showReachableTiles = false; } } else if (event.button.button == SDL_BUTTON_RIGHT) @@ -237,8 +240,18 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) } else { - calculateMovementRange(m_units.at(m_selectedUnit)); - m_units.at(m_selectedUnit).updatePosition(tileX, tileY); + // Calculate movement range as a vector of pairs + auto reachableTiles = calculateMovementRange(m_units.at(m_selectedUnit)); + + // Use std::find to check presence of {tileX, tileY} in reachableTiles vector + if (std::find(reachableTiles.begin(), reachableTiles.end(),std::make_pair(tileX, tileY)) != reachableTiles.end()) + { + m_units.at(m_selectedUnit).updatePosition(tileX, tileY); + } + else + { + std::cout << "Invalid move position!" << std::endl; + } } } else @@ -313,19 +326,15 @@ std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) { wavefrontQueue.push(std::make_tuple(nx, ny, remainingMovement - cost)); } } - } - - std::cout << "Reachable Tiles: " << std::endl; - for (const auto& tile : reachableTiles) { - std::cout << "(" << tile.first << ", " << tile.second << ")" << std::endl; - } - + } return reachableTiles; } -int Level::getMoveCost(TileId type, MovementType movementType) { - // Implementieren Sie die Logik zur Berechnung der Bewegungskosten hier - return 1; // Beispielrückgabe, passen Sie dies nach Bedarf an + + + +int Level::getMoveCost(TileId tileId, MovementType movementType) { + return moveCostTable[static_cast<int>(tileId)][static_cast<int>(movementType)]; } void Level::render(Engine& engine) @@ -345,6 +354,20 @@ void Level::render(Engine& engine) tile.render(engine, RENDERING_SCALE); } + if (m_showReachableTiles) { + SDL_SetRenderDrawColor(engine.renderer(), 255, 255, 0, 128); // Gelb mit leichtem Alpha + + for (const auto& [x, y] : m_reachableTiles) { + SDL_Rect rect = { x * 16 * RENDERING_SCALE, y * 16 * RENDERING_SCALE, + 16 * RENDERING_SCALE, 16 * RENDERING_SCALE }; + SDL_RenderFillRect(engine.renderer(), &rect); + } + + // Reset draw color if required (depends on your rendering setup) + SDL_SetRenderDrawColor(engine.renderer(), 0, 0, 0, 255); + } + + // Buildings for (auto& [id, building] : m_buildings) { diff --git a/src/game/Level.hpp b/src/game/Level.hpp index 53aa865..d44363b 100644 --- a/src/game/Level.hpp +++ b/src/game/Level.hpp @@ -11,10 +11,51 @@ #include <string> #include <unordered_map> #include <vector> +#include <array> + + namespace advanced_wars { + +const int NUM_TILE_IDS = 30; // Aktualisieren, falls weitere IDs hinzugefügt werden +const int NUM_MOVEMENT_TYPES = 6; + + +const std::array<std::array<int, NUM_MOVEMENT_TYPES>, NUM_TILE_IDS> moveCostTable = {{ + // FOOT, WHEELED, TREAD, AIR, SEA, LANDER + { 1, 2, 1, 1, 999, 999 }, // PLAIN + {999, 999, 999, 1, 1, 1 }, // WATER + { 1, 3, 2, 1, 999, 999 }, // FOREST + { 2, 999, 999, 1, 999, 999 }, // MOUNTAIN + { 1, 1, 1, 1, 999, 999 }, // BRIDGE_HORIZONTAL + { 1, 1, 1, 1, 999, 999 }, // STREET_HORIZONTAL + { 1, 1, 1, 1, 999, 999 }, // STREET_VERTICAL + { 1, 1, 1, 1, 999, 999 }, // STREET_CROSSING + { 1, 1, 1, 1, 999, 999 }, // STREET_JUNCTION_RIGHT + { 1, 1, 1, 1, 999, 999 }, // STREET_JUNCTION_LEFT + { 1, 1, 1, 1, 999, 999 }, // STREET_JUNCTION_DOWN + { 1, 1, 1, 1, 999, 999 }, // STREET_JUNCTION_UP + { 1, 1, 1, 1, 999, 999 }, // STREET_CORNER_TOP_LEFT + { 1, 1, 1, 1, 999, 999 }, // STREET_CORNER_TOP_RIGHT + { 1, 1, 1, 1, 999, 999 }, // STREET_CORNER_BOTTOM_LEFT + { 1, 1, 1, 1, 999, 999 }, // STREET_CORNER_BOTTOM_RIGHT + {999, 999, 999, 1, 2, 2 }, // RIFF + {999, 999, 999, 1, 1, 1 }, // CLIFF_TOP + {999, 999, 999, 1, 1, 1 }, // CLIFF_BOTTOM + {999, 999, 999, 1, 1, 1 }, // CLIFF_LEFT + {999, 999, 999, 1, 1, 1 }, // CLIFF_RIGHT + {999, 999, 999, 1, 1, 1 }, // CLIFF_CORNER_TOP_LEFT + {999, 999, 999, 1, 1, 1 }, // CLIFF_CORNER_TOP_RIGHT + {999, 999, 999, 1, 1, 1 }, // CLIFF_CORNER_BOTTOM_LEFT + {999, 999, 999, 1, 1, 1 }, // CLIFF_CORNER_BOTTOM_RIGHT + { 1, 2, 1, 1, 999, 999 }, // CLIFF_INVERSE_CORNER_TOP_LEFT + { 1, 2, 1, 1, 999, 999 }, // CLIFF_INVERSE_CORNER_TOP_RIGHT + { 1, 2, 1, 1, 999, 999 }, // CLIFF_INVERSE_CORNER_BOTTOM_LEFT + { 1, 2, 1, 1, 999, 999 }, // CLIFF_INVERSE_CORNER_BOTTOM_RIGHT +}}; + /** @@ -49,6 +90,9 @@ class Level : public Scene int getMoveCost(TileId type, MovementType movementType); + std::vector<std::pair<int, int>> m_reachableTiles; + + private: std::string m_name; int m_width; @@ -74,6 +118,7 @@ class Level : public Scene bool clickCheckLeft(int mouseX, int mouseY); bool clickCheckRight(int mouseX, int mouseY); + bool m_showReachableTiles; }; } // namespace advanced_wars -- GitLab From 76da6a1f1afd734bf9f9d4745ffa7f6e070ec44e Mon Sep 17 00:00:00 2001 From: Lorenz <lorenz-martin.diel@informatik.hs-fulda.de> Date: Tue, 4 Feb 2025 23:28:52 +0100 Subject: [PATCH 04/10] Made getUnitsInRangeWithDamagePotential function --- src/game/Level.cpp | 119 +++++++++++++++++++++++++++++++++++++-------- src/game/Level.hpp | 9 ++++ src/game/Unit.cpp | 52 ++++++++++++++++---- src/game/Unit.hpp | 3 ++ 4 files changed, 152 insertions(+), 31 deletions(-) diff --git a/src/game/Level.cpp b/src/game/Level.cpp index da19c85..2c2d2e8 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -1,19 +1,19 @@ #include "Level.hpp" #include "Building.hpp" +#include "Config.hpp" #include "Effect.hpp" #include "Engine.hpp" #include "Spritesheet.hpp" #include "Unit.hpp" -#include "Config.hpp" #include "highfive/H5File.hpp" #include "ui/Contextmenu.hpp" #include "ui/Pausemenu.hpp" -#include <queue> #include <SDL.h> #include <algorithm> #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/xml_parser.hpp> #include <iostream> +#include <queue> #include <string> namespace advanced_wars @@ -203,6 +203,49 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) m_reachableTiles = calculateMovementRange(m_units.at(m_selectedUnit)); m_units.at(m_selectedUnit).on_left_click(event); m_showReachableTiles = true; + + std::vector<Unit*> allUnits; + allUnits.reserve(m_units.size()); + + for (auto& [id, unit] : m_units) + { + allUnits.push_back(&unit); // Fügen Sie Zeiger hinzu, nicht Kopien + } + /* + + std::vector<Unit*> attackableTargets = + m_units.at(m_selectedUnit).getUnitsInRangeWithDamagePotential(allUnits); + + m_attackableTiles.clear(); + m_showAttackableTiles = true; + for (Unit* target : attackableTargets) + { + // Füge die Position jedes angreifbaren Ziels hinzu + m_attackableTiles.emplace_back(target->m_x, target->m_y); + }*/ + + std::vector<Unit*> attackableTargets = + m_units.at(m_selectedUnit).getUnitsInRangeWithDamagePotential(allUnits); + + m_attackableTiles.clear(); + m_showAttackableTiles = true; + m_attackableUnitIds.clear(); // <-- Fügen Sie dies hier hinzu + + for (Unit* target : attackableTargets) + { + // Füge die Position jedes angreifbaren Ziels hinzu + m_attackableTiles.emplace_back(target->m_x, target->m_y); + + // Angreifbaren Einheits-ID setzen + for (auto& [id, unit] : m_units) + { + if (&unit == target) + { + m_attackableUnitIds.insert(id); + break; + } + } + } } if (m_selectedBuilding > -1) @@ -217,6 +260,7 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) m_selectedUnit = -1; m_selectedBuilding = -1; m_showReachableTiles = false; + m_showAttackableTiles = false; } } else if (event.button.button == SDL_BUTTON_RIGHT) @@ -231,11 +275,19 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) if (clickCheckRight(tileX, tileY)) { - m_units.at(m_selectedUnit).attack((m_units.at(m_targetedUnit))); + if (m_attackableUnitIds.find(m_targetedUnit) != m_attackableUnitIds.end()) + { + // Attack if the unit is a valid target + m_units.at(m_selectedUnit).attack(m_units.at(m_targetedUnit)); - if (m_units.at(m_selectedUnit).m_health <= 0) + if (m_units.at(m_selectedUnit).m_health <= 0) + { + removeUnit(m_selectedUnit); + } + } + else { - removeUnit(m_selectedUnit); + std::cout << "Invalid attack target!" << std::endl; } } else @@ -244,7 +296,9 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) auto reachableTiles = calculateMovementRange(m_units.at(m_selectedUnit)); // Use std::find to check presence of {tileX, tileY} in reachableTiles vector - if (std::find(reachableTiles.begin(), reachableTiles.end(),std::make_pair(tileX, tileY)) != reachableTiles.end()) + if (std::find( + reachableTiles.begin(), reachableTiles.end(), + std::make_pair(tileX, tileY)) != reachableTiles.end()) { m_units.at(m_selectedUnit).updatePosition(tileX, tileY); } @@ -292,18 +346,21 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) } } -std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) { - std::vector<std::pair<int, int>> reachableTiles; +std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) +{ + std::vector<std::pair<int, int>> reachableTiles; std::queue<std::tuple<int, int, int>> wavefrontQueue; // x, y, remainingMovement wavefrontQueue.push(std::make_tuple(unit.m_x, unit.m_y, unit.m_movementPoints)); std::unordered_map<int, std::unordered_map<int, bool>> visited; - while (!wavefrontQueue.empty()) { + while (!wavefrontQueue.empty()) + { auto [x, y, remainingMovement] = wavefrontQueue.front(); wavefrontQueue.pop(); - if (visited[x][y]) continue; + if (visited[x][y]) + continue; visited[x][y] = true; reachableTiles.emplace_back(x, y); @@ -315,25 +372,27 @@ std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) { { 0, -1} }; - for (const auto& [dx, dy] : directions) { + for (const auto& [dx, dy] : directions) + { int nx = x + dx; int ny = y + dy; - if (nx < 0 || nx >= m_width || ny < 0 || ny >= m_height) continue; // Boundary check + if (nx < 0 || nx >= m_width || ny < 0 || ny >= m_height) + continue; // Boundary check int cost = getMoveCost(m_tiles[ny * m_width + nx].getType(), unit.m_movementType); - if (cost >= 0 && remainingMovement >= cost) { + if (cost >= 0 && remainingMovement >= cost) + { wavefrontQueue.push(std::make_tuple(nx, ny, remainingMovement - cost)); } } - } + } return reachableTiles; } - - -int Level::getMoveCost(TileId tileId, MovementType movementType) { +int Level::getMoveCost(TileId tileId, MovementType movementType) +{ return moveCostTable[static_cast<int>(tileId)][static_cast<int>(movementType)]; } @@ -354,12 +413,15 @@ void Level::render(Engine& engine) tile.render(engine, RENDERING_SCALE); } - if (m_showReachableTiles) { + if (m_showReachableTiles) + { SDL_SetRenderDrawColor(engine.renderer(), 255, 255, 0, 128); // Gelb mit leichtem Alpha - for (const auto& [x, y] : m_reachableTiles) { - SDL_Rect rect = { x * 16 * RENDERING_SCALE, y * 16 * RENDERING_SCALE, - 16 * RENDERING_SCALE, 16 * RENDERING_SCALE }; + for (const auto& [x, y] : m_reachableTiles) + { + SDL_Rect rect = { + x * 16 * RENDERING_SCALE, y * 16 * RENDERING_SCALE, 16 * RENDERING_SCALE, + 16 * RENDERING_SCALE}; SDL_RenderFillRect(engine.renderer(), &rect); } @@ -367,6 +429,21 @@ void Level::render(Engine& engine) SDL_SetRenderDrawColor(engine.renderer(), 0, 0, 0, 255); } + if (m_showAttackableTiles) + { + SDL_SetRenderDrawColor(engine.renderer(), 255, 0, 0, 128); // Rot mit leichtem Alpha + + for (const auto& [x, y] : m_attackableTiles) + { + SDL_Rect rect = { + x * 16 * RENDERING_SCALE, y * 16 * RENDERING_SCALE, 16 * RENDERING_SCALE, + 16 * RENDERING_SCALE}; + SDL_RenderFillRect(engine.renderer(), &rect); + } + + // Optionale Rücksetzung der Zeichenfarbe + SDL_SetRenderDrawColor(engine.renderer(), 0, 0, 0, 255); + } // Buildings for (auto& [id, building] : m_buildings) diff --git a/src/game/Level.hpp b/src/game/Level.hpp index d44363b..0799a7f 100644 --- a/src/game/Level.hpp +++ b/src/game/Level.hpp @@ -12,6 +12,8 @@ #include <unordered_map> #include <vector> #include <array> +#include <unordered_set> + @@ -91,6 +93,10 @@ class Level : public Scene int getMoveCost(TileId type, MovementType movementType); std::vector<std::pair<int, int>> m_reachableTiles; + + std::vector<std::pair<int, int>> m_attackableTiles; + + private: @@ -119,6 +125,9 @@ class Level : public Scene bool clickCheckLeft(int mouseX, int mouseY); bool clickCheckRight(int mouseX, int mouseY); bool m_showReachableTiles; + bool m_showAttackableTiles; + + std::unordered_set<int> m_attackableUnitIds; }; } // namespace advanced_wars diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index e013e97..b0d7824 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -228,17 +228,49 @@ void Unit::on_left_click(SDL_Event event) std::cout << "Left-button pressed on unit: " << this->m_health << std::endl; } -bool Unit::inRange(Unit& enemy) +std::vector<Unit*> Unit::getUnitsInRangeWithDamagePotential(const std::vector<Unit*>& allUnits) { - if (this->m_x == enemy.m_x) - { - return abs(this->m_y - enemy.m_y) <= this->m_range; - } - else if (this->m_y == enemy.m_y) - { - return abs(this->m_x - enemy.m_x) <= this->m_range; + std::vector<Unit*> unitsInRangeWithDamage; + + for (Unit* unit : allUnits) + { // Iteriere über Zeiger + // Ignoriere sich selbst + if (unit == this) + { + continue; + } + + int distanceX = std::abs(unit->m_x - m_x); + int distanceY = std::abs(unit->m_y - m_y); + + // Manhattan-Distanz Berechnung + int distance = distanceX + distanceY; + if (distance >= m_minRange && distance <= m_maxRange) + { + // Prüfen ob Schaden möglich ist + auto primaryDamageIter = m_primaryWeapon.m_damage.find(unit->m_id); + auto secondaryDamageIter = m_secondaryWeapon.m_damage.find(unit->m_id); + + bool canDealDamage = false; + + // Prüfen, ob Primärwaffe Schaden machen kann + if (primaryDamageIter != m_primaryWeapon.m_damage.end() && m_ammo > 0) + { + canDealDamage = true; + } + // Prüfen, ob Sekundärwaffe Schaden machen kann + if (secondaryDamageIter != m_secondaryWeapon.m_damage.end()) + { + canDealDamage = true; + } + + if (canDealDamage) + { + unitsInRangeWithDamage.push_back(unit); + } + } } - return false; -} + return unitsInRangeWithDamage; +} } // namespace advanced_wars \ No newline at end of file diff --git a/src/game/Unit.hpp b/src/game/Unit.hpp index b9f645b..d373076 100644 --- a/src/game/Unit.hpp +++ b/src/game/Unit.hpp @@ -130,6 +130,9 @@ class Unit */ void on_left_click(SDL_Event event); + std::vector<Unit*> getUnitsInRangeWithDamagePotential(const std::vector<Unit*>& allUnits); + + private: UnitFaction m_faction; UnitId m_id; -- GitLab From d38ddd7091fa00107c23113cf11b489526b162aa Mon Sep 17 00:00:00 2001 From: Lorenz <lorenz-martin.diel@informatik.hs-fulda.de> Date: Wed, 5 Feb 2025 17:11:12 +0100 Subject: [PATCH 05/10] updated attack function + ammo is deducted + inrange check for retaliation --- src/game/Level.cpp | 24 ++------- src/game/Unit.cpp | 128 ++++++++++++++++++++++++--------------------- src/game/Unit.hpp | 4 ++ 3 files changed, 75 insertions(+), 81 deletions(-) diff --git a/src/game/Level.cpp b/src/game/Level.cpp index 2c2d2e8..dd737e9 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -127,14 +127,11 @@ bool Level::clickCheckRight(int tileX, int tileY) bool Level::selectUnit(int tileX, int tileY) { - // std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; for (auto& [id, unit] : m_units) { if (unit.m_x == tileX && unit.m_y == tileY) { - // std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; - m_selectedUnit = id; return true; } @@ -146,14 +143,11 @@ bool Level::selectUnit(int tileX, int tileY) bool Level::targetUnit(int tileX, int tileY) { - // std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; for (auto& [id, unit] : m_units) { if (unit.m_x == tileX && unit.m_y == tileY) { - // std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; - m_targetedUnit = id; return true; } @@ -209,27 +203,15 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) for (auto& [id, unit] : m_units) { - allUnits.push_back(&unit); // Fügen Sie Zeiger hinzu, nicht Kopien + allUnits.push_back(&unit); } - /* - - std::vector<Unit*> attackableTargets = - m_units.at(m_selectedUnit).getUnitsInRangeWithDamagePotential(allUnits); - - m_attackableTiles.clear(); - m_showAttackableTiles = true; - for (Unit* target : attackableTargets) - { - // Füge die Position jedes angreifbaren Ziels hinzu - m_attackableTiles.emplace_back(target->m_x, target->m_y); - }*/ - + std::vector<Unit*> attackableTargets = m_units.at(m_selectedUnit).getUnitsInRangeWithDamagePotential(allUnits); m_attackableTiles.clear(); m_showAttackableTiles = true; - m_attackableUnitIds.clear(); // <-- Fügen Sie dies hier hinzu + m_attackableUnitIds.clear(); for (Unit* target : attackableTargets) { diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index b0d7824..796730d 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -105,78 +105,86 @@ void Unit::render(Engine& engine, int scale) void Unit::attack(Unit& enemy) { - // Angenommen, primary_weapon und secondary_weapon wurden bereits korrekt - // initialisiert + int attacker_damage_value = calculateDamage(enemy); - auto primary_weapon_damage_it = m_primaryWeapon.m_damage.find(enemy.m_id); - auto secondary_weapon_damage_it = m_secondaryWeapon.m_damage.find(enemy.m_id); - - int attacker_damage_value = 0; - - // Die Waffe mit dem höchsten Schaden wählen - if (secondary_weapon_damage_it != m_secondaryWeapon.m_damage.end()) + if (attacker_damage_value > 0) { - attacker_damage_value = secondary_weapon_damage_it->second; - } + performAttack(enemy, attacker_damage_value); + std::cout << "Enemy health after attack: " << enemy.m_health << std::endl; - if (primary_weapon_damage_it != m_primaryWeapon.m_damage.end()) - { - if (primary_weapon_damage_it->second > attacker_damage_value) + // Check if the enemy is still alive for counter-attack + if (enemy.m_health > 0) { - // Munitionsabzug sollte hier erfolgen, falls zutreffend - attacker_damage_value = primary_weapon_damage_it->second; + // Check if the enemy is within attack range + int distanceX = std::abs(enemy.m_x - m_x); + int distanceY = std::abs(enemy.m_y - m_y); + int distance = distanceX + distanceY; + + if (distance >= enemy.m_minRange && distance <= enemy.m_maxRange) + { + // Now, they are reversed for the counter-attack + int defender_damage_value = enemy.calculateDamage(*this); + if (defender_damage_value > 0) + { + enemy.performAttack(*this, defender_damage_value); + std::cout << "Ally health after retaliation: " << this->m_health << std::endl; + } + } + else + { + std::cout << "Enemy out of range for counter-attack." << std::endl; + } } } - - if (attacker_damage_value == 0) + else { std::cout << "No damage value found for attack from unit " << static_cast<int>(m_id) << " against unit " << static_cast<int>(enemy.m_id) << std::endl; } - else +} + +int Unit::calculateDamage(Unit& target) +{ + // Pointers to Weapon objects + Weapon* primaryWeapon = &m_primaryWeapon; + Weapon* secondaryWeapon = &m_secondaryWeapon; + + // Find the corresponding damage values + auto primary_damage_it = primaryWeapon->m_damage.find(target.m_id); + auto secondary_damage_it = secondaryWeapon->m_damage.find(target.m_id); + + int damage_value = 0; + + // Calculate damage using secondary weapon if available + if (secondary_damage_it != secondaryWeapon->m_damage.end()) { - int off_damage = attacker_damage_value * (static_cast<float>(m_health) / m_maxHealth); - enemy.m_health -= off_damage; - enemy.m_health = std::max( - 0, - enemy.m_health); // Sicherstellen, dass die Gesundheit nicht negativ wird - std::cout << "Enemy health after attack: " << enemy.m_health << std::endl; + damage_value = secondary_damage_it->second; + } - // Prüfen, ob der Gegner noch am Leben ist um zurückzuschlagen - if (enemy.m_health > 0) + // Calculate damage using primary weapon if higher and ammo is available + if (primary_damage_it != primaryWeapon->m_damage.end()) + { + // Check ammo correctly + int& ammo = m_ammo; + + if (primary_damage_it->second > damage_value && ammo > 0) { - // Weapon tables for the defender - auto defender_primary_weapon_damage_it = enemy.m_primaryWeapon.m_damage.find(m_id); - auto defender_secondary_weapon_damage_it = enemy.m_secondaryWeapon.m_damage.find(m_id); + ammo -= 1; + damage_value = primary_damage_it->second; + std::cout << " ammo = " << ammo << std::endl; + } + } - int defender_damage_value = 0; // Declare outside for later use + return damage_value; +} - // Determine the damage value for the defender - if (defender_secondary_weapon_damage_it != enemy.m_secondaryWeapon.m_damage.end()) - { - defender_damage_value = defender_secondary_weapon_damage_it->second; - } - if (defender_primary_weapon_damage_it != enemy.m_primaryWeapon.m_damage.end()) - { - if (defender_primary_weapon_damage_it->second > defender_damage_value) - { - // Munitionsabzug für primäre Waffe, falls zutreffend - defender_damage_value = defender_primary_weapon_damage_it->second; - } - } - // If a valid damage value was determined for retaliation - if (defender_damage_value > 0) - { - int def_damage = static_cast<int>( - defender_damage_value * static_cast<float>(enemy.m_health) / enemy.m_maxHealth); - this->m_health -= def_damage; - this->m_health = std::max(0, this->m_health); // Safeguard against negative health - std::cout << "Ally health after retaliation: " << this->m_health << std::endl; - } - } - } +void Unit::performAttack(Unit& target, int damage) +{ + int effective_damage = damage * (static_cast<float>(m_health) / m_maxHealth); + target.m_health -= effective_damage; + target.m_health = std::max(0, target.m_health); } void Unit::updatePosition(int posX, int posY) @@ -233,8 +241,8 @@ std::vector<Unit*> Unit::getUnitsInRangeWithDamagePotential(const std::vector<Un std::vector<Unit*> unitsInRangeWithDamage; for (Unit* unit : allUnits) - { // Iteriere über Zeiger - // Ignoriere sich selbst + { //Iterate over all units + // except itself if (unit == this) { continue; @@ -243,7 +251,7 @@ std::vector<Unit*> Unit::getUnitsInRangeWithDamagePotential(const std::vector<Un int distanceX = std::abs(unit->m_x - m_x); int distanceY = std::abs(unit->m_y - m_y); - // Manhattan-Distanz Berechnung + int distance = distanceX + distanceY; if (distance >= m_minRange && distance <= m_maxRange) { @@ -254,12 +262,12 @@ std::vector<Unit*> Unit::getUnitsInRangeWithDamagePotential(const std::vector<Un bool canDealDamage = false; // Prüfen, ob Primärwaffe Schaden machen kann - if (primaryDamageIter != m_primaryWeapon.m_damage.end() && m_ammo > 0) + if (primaryDamageIt != m_primaryWeapon.m_damage.end() && m_ammo > 0) { canDealDamage = true; } // Prüfen, ob Sekundärwaffe Schaden machen kann - if (secondaryDamageIter != m_secondaryWeapon.m_damage.end()) + if (secondaryDamageIt != m_secondaryWeapon.m_damage.end()) { canDealDamage = true; } diff --git a/src/game/Unit.hpp b/src/game/Unit.hpp index d373076..e855cd8 100644 --- a/src/game/Unit.hpp +++ b/src/game/Unit.hpp @@ -104,6 +104,10 @@ class Unit void attack(Unit& enemy); + void performAttack(Unit& target, int damage); + + int calculateDamage(Unit& target); + /* @params Takes the desired position of the unit and updates its values This will teleport the unit, there is no smooth transition between tiles -- GitLab From 2af8c4d3b7a08887818929edb695cef2a3353986 Mon Sep 17 00:00:00 2001 From: Lorenz <lorenz-martin.diel@informatik.hs-fulda.de> Date: Wed, 5 Feb 2025 17:35:39 +0100 Subject: [PATCH 06/10] updatet calculateMovementRange function only none occupied tiles are now reachable --- src/game/Level.cpp | 29 +++++++++++++++++++++-------- src/game/Unit.cpp | 4 ++-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/game/Level.cpp b/src/game/Level.cpp index dd737e9..6633236 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -330,7 +330,7 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) { - std::vector<std::pair<int, int>> reachableTiles; + std::vector<std::pair<int, int>> reachableTiles; std::queue<std::tuple<int, int, int>> wavefrontQueue; // x, y, remainingMovement wavefrontQueue.push(std::make_tuple(unit.m_x, unit.m_y, unit.m_movementPoints)); @@ -341,17 +341,29 @@ std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) auto [x, y, remainingMovement] = wavefrontQueue.front(); wavefrontQueue.pop(); + // Check if this tile has been visited already if (visited[x][y]) continue; visited[x][y] = true; - reachableTiles.emplace_back(x, y); + // Check if a unit is on the current tile, skip adding it if true + bool isOccupied = false; + for (const auto& [id, otherUnit] : m_units) + { + if (otherUnit.m_x == x && otherUnit.m_y == y && id != m_selectedUnit) + { + isOccupied = true; + break; + } + } + // Add to reachable tiles only if not occupied + if (!isOccupied) + { + reachableTiles.emplace_back(x, y); + } static const std::vector<std::pair<int, int>> directions = { - { 1, 0}, - {-1, 0}, - { 0, 1}, - { 0, -1} + {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; for (const auto& [dx, dy] : directions) @@ -373,6 +385,7 @@ std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) return reachableTiles; } + int Level::getMoveCost(TileId tileId, MovementType movementType) { return moveCostTable[static_cast<int>(tileId)][static_cast<int>(movementType)]; @@ -407,7 +420,7 @@ void Level::render(Engine& engine) SDL_RenderFillRect(engine.renderer(), &rect); } - // Reset draw color if required (depends on your rendering setup) + SDL_SetRenderDrawColor(engine.renderer(), 0, 0, 0, 255); } @@ -423,7 +436,7 @@ void Level::render(Engine& engine) SDL_RenderFillRect(engine.renderer(), &rect); } - // Optionale Rücksetzung der Zeichenfarbe + SDL_SetRenderDrawColor(engine.renderer(), 0, 0, 0, 255); } diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 796730d..6b441df 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -256,8 +256,8 @@ std::vector<Unit*> Unit::getUnitsInRangeWithDamagePotential(const std::vector<Un if (distance >= m_minRange && distance <= m_maxRange) { // Prüfen ob Schaden möglich ist - auto primaryDamageIter = m_primaryWeapon.m_damage.find(unit->m_id); - auto secondaryDamageIter = m_secondaryWeapon.m_damage.find(unit->m_id); + auto primaryDamageIt = m_primaryWeapon.m_damage.find(unit->m_id); + auto secondaryDamageIt = m_secondaryWeapon.m_damage.find(unit->m_id); bool canDealDamage = false; -- GitLab From 96c66e4b70a034f5cc0b981d0d3f3afa731a9958 Mon Sep 17 00:00:00 2001 From: Lorenz <lorenz-martin.diel@informatik.hs-fulda.de> Date: Wed, 5 Feb 2025 18:23:56 +0100 Subject: [PATCH 07/10] Small Changes --- src/game/Level.cpp | 35 ++++++---- src/game/Unit.cpp | 2 + src/game/Unit.hpp | 170 ++++++++++++++++++++++++++++----------------- 3 files changed, 130 insertions(+), 77 deletions(-) diff --git a/src/game/Level.cpp b/src/game/Level.cpp index 6633236..00f140e 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -199,13 +199,12 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) m_showReachableTiles = true; std::vector<Unit*> allUnits; - allUnits.reserve(m_units.size()); for (auto& [id, unit] : m_units) { allUnits.push_back(&unit); } - + std::vector<Unit*> attackableTargets = m_units.at(m_selectedUnit).getUnitsInRangeWithDamagePotential(allUnits); @@ -266,6 +265,10 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) { removeUnit(m_selectedUnit); } + if (m_units.at(m_targetedUnit).m_health <= 0) + { + removeUnit(m_targetedUnit); + } } else { @@ -277,16 +280,24 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) // Calculate movement range as a vector of pairs auto reachableTiles = calculateMovementRange(m_units.at(m_selectedUnit)); - // Use std::find to check presence of {tileX, tileY} in reachableTiles vector - if (std::find( - reachableTiles.begin(), reachableTiles.end(), - std::make_pair(tileX, tileY)) != reachableTiles.end()) + bool isReachable = false; + + for (const auto& pos : reachableTiles) + { + if (pos == std::make_pair(tileX, tileY)) + { + isReachable = true; + break; + } + } + + if (isReachable) { m_units.at(m_selectedUnit).updatePosition(tileX, tileY); } else { - std::cout << "Invalid move position!" << std::endl; + std::cout << "Ungültige Bewegungsposition!" << std::endl; } } } @@ -330,7 +341,7 @@ void Level::handleEvent(Engine& engine, SDL_Event& event) std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) { - std::vector<std::pair<int, int>> reachableTiles; + std::vector<std::pair<int, int>> reachableTiles; std::queue<std::tuple<int, int, int>> wavefrontQueue; // x, y, remainingMovement wavefrontQueue.push(std::make_tuple(unit.m_x, unit.m_y, unit.m_movementPoints)); @@ -363,7 +374,10 @@ std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) } static const std::vector<std::pair<int, int>> directions = { - {1, 0}, {-1, 0}, {0, 1}, {0, -1} + { 1, 0}, + {-1, 0}, + { 0, 1}, + { 0, -1} }; for (const auto& [dx, dy] : directions) @@ -385,7 +399,6 @@ std::vector<std::pair<int, int>> Level::calculateMovementRange(Unit& unit) return reachableTiles; } - int Level::getMoveCost(TileId tileId, MovementType movementType) { return moveCostTable[static_cast<int>(tileId)][static_cast<int>(movementType)]; @@ -420,7 +433,6 @@ void Level::render(Engine& engine) SDL_RenderFillRect(engine.renderer(), &rect); } - SDL_SetRenderDrawColor(engine.renderer(), 0, 0, 0, 255); } @@ -436,7 +448,6 @@ void Level::render(Engine& engine) SDL_RenderFillRect(engine.renderer(), &rect); } - SDL_SetRenderDrawColor(engine.renderer(), 0, 0, 0, 255); } diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 6b441df..1c03e31 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -42,6 +42,8 @@ Unit::Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state, Config m_secondaryWeapon = Weapon(config.getUnitSecondaryWeapon(id), secondaryDamage); } + + void Unit::render(Engine& engine, int scale) { Spritesheet* spritesheet = engine.getSpritesheet(); diff --git a/src/game/Unit.hpp b/src/game/Unit.hpp index e855cd8..b58bb6f 100644 --- a/src/game/Unit.hpp +++ b/src/game/Unit.hpp @@ -42,7 +42,7 @@ enum class UnitId LANDER = 17, SUBMARINE = 18, - FIRST= INFANTERY, + FIRST = INFANTERY, LAST = SUBMARINE }; @@ -73,94 +73,134 @@ class Unit public: int m_x; int m_y; - int m_health; // health equals max_health at construction + int m_health; // Current health of the unit, initialized to max health at construction. - int m_movementPoints; - MovementType m_movementType; + int m_movementPoints; // The number of tiles this unit can move per turn. + MovementType m_movementType; // The type of movement this unit has (e.g., foot, wheeled). + /** + * Constructor for Unit. + * Initializes the unit's position, faction, identifier, state, and configuration settings. + */ Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state, Config& config); - ~Unit() - { - // Assuming that the destruktion of a unit triggers events - } + /** + * Destructor for Unit. + * Handles any cleanup necessary when a unit is destroyed. + * (Currently assumes this triggers certain game events, though not explicitly detailed + * here.) + */ + ~Unit(){}; + + /** + * Renders the unit on the game screen. + * Uses the engine's rendering capabilities to draw the unit based on its state and faction. + * + * @param engine The game engine responsible for rendering. + * @param scale Scaling factor for rendering the unit. + */ void render(Engine& engine, int scale); - /* - Check if attacker is in Range to initiate combat - TODO: This should probably tie back into rendering the units differently - If a unit is selected, it should call inRange on all other enemy units on the field - */ - + /** + * Determines if another unit (enemy) is within attack range. + * Checks for the range based on the unit's weapon capabilities. + * + * @param enemy The enemy unit to check range against. + * @return true if the enemy is in range, false otherwise. + */ bool inRange(Unit& enemy); - /* - The attacker will move towards the defender and thus initiate combat - @params Takes a reference to the defender - - Will Update the health for both units - Attacker deals damage to the defender first - */ - + /** + * Initiates an attack on a specified enemy unit. + * Calculates damage and updates health values for both the attacker and defender. + * Attacker damages the enemy first; if the enemy survives, a counter-attack may occur. + * + * @param enemy The unit being attacked. + */ void attack(Unit& enemy); + /** + * Performs the calculated damage on a target unit, adjusting its health accordingly. + * Considers this unit's current health for scaling damage. + * + * @param target The target unit receiving damage. + * @param damage The amount of damage calculated to be applied. + */ void performAttack(Unit& target, int damage); + /** + * Calculates the potential damage this unit can inflict on a target. + * Considers primary and secondary weapons' damage tables, and checks ammunition + * availability. + * + * @param target The unit to calculate damage against. + * @return The calculated damage value. + */ int calculateDamage(Unit& target); - /* - @params Takes the desired position of the unit and updates its values - This will teleport the unit, there is no smooth transition between tiles - */ + /** + * Updates this unit's position on the game field. + * Changes the internal position and recalculates the unit's state (e.g., direction it's + * facing) based on movement to a new position. + * + * @param posX The new x-coordinate for the unit. + * @param posY The new y-coordinate for the unit. + */ void updatePosition(int posX, int posY); - /* - This function needs to be able to determine the possible movement-paths the unit can take - MUST take into consideration that different units behave differently on certain terrain - MUST show all movements possible - Requires dijkstras algorithm and the required graphs - */ + /** + * Determines potential movement paths based on the unit's movement type and current + * terrain. (Intended to use Dijkstra's algorithm, though implementation details are omitted + * here.) + */ void calculateMovement(); - /* - This function calculates the difference between current position and desired position. - It updates the unit_state accordingly, so that the units face the correct direction. - */ + /** + * Recalculates and updates the unit's state based on movement direction. + * Ensures that the unit faces the correct direction after a move. + * + * @param posX The x-coordinate of the desired position. + * @param posY The y-coordinate of the desired position. + */ void calcState(int posX, int posY); - /* - This function will be called by an external event-handler, eventually. - It should start displaying standard unit information, such as UI and move_range - */ + /** + * Processes a left-click event on this unit. + * Typically triggers display of unit information (e.g., UI and movement range). + * + * @param event SDL event captured, specifically mouse click event. + */ void on_left_click(SDL_Event event); + /** + * Retrieves units within range that this unit can deal damage to. + * Considers all units provided in 'allUnits', excluding itself, and checks movement and + * weapon range. + * + * @param allUnits Vector of pointers to all units on the field to check against. + * @return Vector of pointers to units in range that can be engaged. + */ std::vector<Unit*> getUnitsInRangeWithDamagePotential(const std::vector<Unit*>& allUnits); - private: - UnitFaction m_faction; - UnitId m_id; - UnitState m_state; - - int m_maxHealth; // max_health required for damage_scaling - int m_range; - /* - int m_fuel; - int m_maxFuel;*/ - - bool m_hasMoved; - bool m_hasAttacked; - bool m_isSelected; - bool m_isTargeted; - - Weapon m_secondaryWeapon; - Weapon m_primaryWeapon; - - int m_cost; - - int m_ammo; - int m_minRange; - int m_maxRange; -}; + UnitFaction m_faction; // The faction to which this unit belongs. + UnitId m_id; // The identifier for the unit type. + UnitState m_state; // The current state of the unit (idle, moving, etc.). + int m_maxHealth; // The maximum health of the unit. + int m_range; // Possible range for future use, depending on specific unit abilities. + + bool m_hasMoved; // Indicates whether the unit has moved this turn. + bool m_hasAttacked; // Indicates whether the unit has attacked this turn. + bool m_isSelected; // Indicates whether the unit is currently selected. + bool m_isTargeted; // Indicates whether the unit is currently targeted by an enemy. + + Weapon m_secondaryWeapon; // The unit's secondary weapon. + Weapon m_primaryWeapon; // The unit's primary weapon. + + int m_cost; // The cost associated with deploying this unit. + int m_ammo; // The amount of available ammo for attacks. + int m_minRange; // The minimum range of the unit's attack capability. + int m_maxRange; // The maximum range of the unit's attack capability. +}; } // namespace advanced_wars \ No newline at end of file -- GitLab From dfc7bff529ba07bcfa266d65ed800dc0570f9bfd Mon Sep 17 00:00:00 2001 From: Frederik <frederik@prasch.de> Date: Thu, 6 Feb 2025 01:24:29 +0100 Subject: [PATCH 08/10] Fix circular import by giving Config Ownership of all enums --- src/game/Building.hpp | 18 ------- src/game/Config.cpp | 44 +++++++++------- src/game/Config.hpp | 114 +++++++++++++++++++++++++++++++++++++++++- src/game/Engine.cpp | 2 +- src/game/Engine.hpp | 6 +++ src/game/Level.cpp | 1 - src/game/Level.hpp | 12 ++--- src/game/Tile.hpp | 38 +------------- src/game/Unit.hpp | 61 ++-------------------- 9 files changed, 153 insertions(+), 143 deletions(-) diff --git a/src/game/Building.hpp b/src/game/Building.hpp index 2bceaa9..8dcb5cd 100644 --- a/src/game/Building.hpp +++ b/src/game/Building.hpp @@ -8,24 +8,6 @@ namespace advanced_wars { -enum class BuildingFaction -{ - URED = 0, - UBLUE = 1, - UGREEN = 2, - UYELLOW = 3, - UPURPLE = 4, -}; - -enum class BuildingId -{ - HEADQUARTER = 0, - CITY = 1, - FACTORY = 2, - AIRPORT = 3, - PORT = 4, -}; - class Building { public: diff --git a/src/game/Config.cpp b/src/game/Config.cpp index 2a01848..b68d38f 100644 --- a/src/game/Config.cpp +++ b/src/game/Config.cpp @@ -11,7 +11,7 @@ namespace advanced_wars { -Config::Config(const std::string& filename) +Config::Config(std::string filename) { namespace pt = boost::property_tree; pt::ptree tree; @@ -220,27 +220,33 @@ std::string Config::getUnitSecondaryWeapon(UnitId id) const return ""; } -std::optional<int> Config::getUnitPrimaryWeaponDamage(UnitId attackerId, UnitId targetId) const { - auto attackerMapIt = m_primaryWeaponDamage.find(attackerId); - if (attackerMapIt != m_primaryWeaponDamage.end()) { - auto damageIt = attackerMapIt->second.find(targetId); - if (damageIt != attackerMapIt->second.end()) { - return damageIt->second; - } +std::optional<int> Config::getUnitPrimaryWeaponDamage(UnitId attackerId, UnitId targetId) const +{ + auto attackerMapIt = m_primaryWeaponDamage.find(attackerId); + if (attackerMapIt != m_primaryWeaponDamage.end()) + { + auto damageIt = attackerMapIt->second.find(targetId); + if (damageIt != attackerMapIt->second.end()) + { + return damageIt->second; } - // Kein spezifischer Schaden vorhanden - return std::nullopt; } + // Kein spezifischer Schaden vorhanden + return std::nullopt; +} -std::optional<int> Config::getUnitSecondaryWeaponDamage(UnitId attackerId, UnitId targetId) const { - auto attackerMapIt = m_secondaryWeaponDamage.find(attackerId); - if (attackerMapIt != m_secondaryWeaponDamage.end()) { - auto damageIt = attackerMapIt->second.find(targetId); - if (damageIt != attackerMapIt->second.end()) { - return damageIt->second; - } +std::optional<int> Config::getUnitSecondaryWeaponDamage(UnitId attackerId, UnitId targetId) const +{ + auto attackerMapIt = m_secondaryWeaponDamage.find(attackerId); + if (attackerMapIt != m_secondaryWeaponDamage.end()) + { + auto damageIt = attackerMapIt->second.find(targetId); + if (damageIt != attackerMapIt->second.end()) + { + return damageIt->second; } - // Kein spezifischer Schaden vorhanden - return std::nullopt; } + // Kein spezifischer Schaden vorhanden + return std::nullopt; +} } // namespace advanced_wars \ No newline at end of file diff --git a/src/game/Config.hpp b/src/game/Config.hpp index d3f43fd..b0289ce 100644 --- a/src/game/Config.hpp +++ b/src/game/Config.hpp @@ -1,14 +1,124 @@ #pragma once -#include "Unit.hpp" // Include for UnitId and MovementType #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/xml_parser.hpp> +#include <optional> #include <stdexcept> #include <string> #include <unordered_map> namespace advanced_wars { +/* ENUMS FOR GLOBAL USE*/ +enum class BuildingFaction +{ + URED = 0, + UBLUE = 1, + UGREEN = 2, + UYELLOW = 3, + UPURPLE = 4, +}; + +enum class BuildingId +{ + HEADQUARTER = 0, + CITY = 1, + FACTORY = 2, + AIRPORT = 3, + PORT = 4, +}; + +enum class UnitFaction +{ + URED = 0, + UBLUE = 1, + UGREEN = 2, + UYELLOW = 3, + UPURPLE = 4, +}; + +enum class UnitId +{ + INFANTERY = 0, + MECHANIZED_INFANTERY = 1, + RECON = 2, + MEDIUM_TANK = 3, + HEAVY_TANK = 4, + NEO_TANK = 5, + APC = 6, + ANTI_AIR_TANK = 7, + ARTILLERY = 8, + ROCKET_ARTILLERY = 9, + ANTI_AIR_MISSILE_LAUNCHER = 10, + FIGHTER = 11, + BOMBER = 12, + BATTLE_HELICOPTER = 13, + TRANSPORT_HELICOPTER = 14, + BATTLESHIP = 15, + CRUISER = 16, + LANDER = 17, + SUBMARINE = 18, + + FIRST = INFANTERY, + LAST = SUBMARINE +}; + +enum class UnitState +{ + IDLE = 0, + UNAVAILABLE = 1, + MOVEMENTLEFT = 2, + MOVEMENTRIGHT = 3, + MOVEMENTDOWN = 4, + MOVEMENTUP = 5, +}; + +enum class MovementType +{ + FOOT = 0, + WHEELED = 1, + TREAD = 2, + AIR = 3, + SEA = 4, + LANDER = 5, +}; + +enum class TileId +{ + PLAIN = 0, + WATER = 1, + FOREST = 2, + MOUNTAIN = 3, + BRIDGE_HORIZONTAL = 4, + BRIDGE_VERTICAL = 5, + STREET_HORIZONTAL = 6, + STREET_VERTICAL = 7, + STREET_CROSSING = 8, + STREET_JUNCTION_RIGHT = 9, + STREET_JUNCTION_LEFT = 10, + STREET_JUNCTION_DOWN = 11, + STREET_JUNCTION_UP = 12, + STREET_CORNER_TOP_LEFT = 13, + STREET_CORNER_TOP_RIGHT = 14, + STREET_CORNER_BOTTOM_LEFT = 15, + STREET_CORNER_BOTTOM_RIGHT = 16, + RIFF = 17, + CLIFF_TOP = 18, + CLIFF_BOTTOM = 19, + CLIFF_LEFT = 20, + CLIFF_RIGHT = 21, + CLIFF_CORNER_TOP_LEFT = 22, + CLIFF_CORNER_TOP_RIGHT = 23, + CLIFF_CORNER_BOTTOM_LEFT = 24, + CLIFF_CORNER_BOTTOM_RIGHT = 25, + CLIFF_INVERSE_CORNER_TOP_LEFT = 26, + CLIFF_INVERSE_CORNER_TOP_RIGHT = 27, + CLIFF_INVERSE_CORNER_BOTTOM_LEFT = 28, + CLIFF_INVERSE_CORNER_BOTTOM_RIGHT = 29, +}; + +/* END OF ALL ENUMS*/ + /** * @class Config * @brief Parses and stores unit configuration data from an XML file. @@ -28,7 +138,7 @@ class Config * @param filename Path to the XML configuration file. * @throws std::runtime_error if the file cannot be read or parsed. */ - explicit Config(const std::string& filename); + Config(std::string filename); /** @brief Retrieves the cost of a given unit type. */ int getUnitCost(UnitId id) const; diff --git a/src/game/Engine.cpp b/src/game/Engine.cpp index 7639be5..b1c7039 100644 --- a/src/game/Engine.cpp +++ b/src/game/Engine.cpp @@ -15,7 +15,7 @@ namespace advanced_wars { -Engine::Engine(Window& window) : m_window(window), m_quit(false) +Engine::Engine(Window& window) : m_window(window), m_quit(false), m_unitConfig("../config.xml") { this->m_SDLRenderer = SDL_CreateRenderer( diff --git a/src/game/Engine.hpp b/src/game/Engine.hpp index 4ecbf8b..feb8447 100644 --- a/src/game/Engine.hpp +++ b/src/game/Engine.hpp @@ -1,5 +1,6 @@ #pragma once +#include "Config.hpp" #include "SDL_events.h" #include "Scene.hpp" #include "Spritesheet.hpp" @@ -15,6 +16,7 @@ namespace advanced_wars // Forward declaration class Scene; +class Config; /** * @brief The main window of the game @@ -47,6 +49,8 @@ class Engine int getStage(); + Config& getUnitConfig(); + void render(); SDL_Renderer* renderer(); @@ -62,6 +66,8 @@ class Engine bool m_quit; int m_stage; + + Config m_unitConfig; }; } // namespace advanced_wars diff --git a/src/game/Level.cpp b/src/game/Level.cpp index fa2ff42..d08f68e 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -149,7 +149,6 @@ int Level::selectBuilding(int tileX, int tileY) return -1; } - void Level::handleEvent(Engine& engine, SDL_Event& event) { switch (m_state) diff --git a/src/game/Level.hpp b/src/game/Level.hpp index ba69b98..f3f25a1 100644 --- a/src/game/Level.hpp +++ b/src/game/Level.hpp @@ -7,17 +7,14 @@ #include "Tile.hpp" #include "Unit.hpp" #include "ui/Contextmenu.hpp" -#include "ui/TileMarker.hpp" #include "ui/Recruitingmenu.hpp" +#include "ui/TileMarker.hpp" #include <SDL.h> +#include <array> #include <string> #include <unordered_map> -#include <vector> -#include <array> #include <unordered_set> - - - +#include <vector> namespace advanced_wars { @@ -120,9 +117,6 @@ class Level : public Scene std::vector<std::pair<int, int>> m_attackableTiles; - - - private: std::string m_name; int m_width; diff --git a/src/game/Tile.hpp b/src/game/Tile.hpp index 7a3d838..ff137b8 100644 --- a/src/game/Tile.hpp +++ b/src/game/Tile.hpp @@ -6,40 +6,6 @@ namespace advanced_wars { -enum class TileId -{ - PLAIN = 0, - WATER = 1, - FOREST = 2, - MOUNTAIN = 3, - BRIDGE_HORIZONTAL = 4, - BRIDGE_VERTICAL = 5, - STREET_HORIZONTAL = 6, - STREET_VERTICAL = 7, - STREET_CROSSING = 8, - STREET_JUNCTION_RIGHT = 9, - STREET_JUNCTION_LEFT = 10, - STREET_JUNCTION_DOWN = 11, - STREET_JUNCTION_UP = 12, - STREET_CORNER_TOP_LEFT = 13, - STREET_CORNER_TOP_RIGHT = 14, - STREET_CORNER_BOTTOM_LEFT = 15, - STREET_CORNER_BOTTOM_RIGHT = 16, - RIFF = 17, - CLIFF_TOP = 18, - CLIFF_BOTTOM = 19, - CLIFF_LEFT = 20, - CLIFF_RIGHT = 21, - CLIFF_CORNER_TOP_LEFT = 22, - CLIFF_CORNER_TOP_RIGHT = 23, - CLIFF_CORNER_BOTTOM_LEFT = 24, - CLIFF_CORNER_BOTTOM_RIGHT = 25, - CLIFF_INVERSE_CORNER_TOP_LEFT = 26, - CLIFF_INVERSE_CORNER_TOP_RIGHT = 27, - CLIFF_INVERSE_CORNER_BOTTOM_LEFT = 28, - CLIFF_INVERSE_CORNER_BOTTOM_RIGHT = 29, -}; - class Tile { public: @@ -48,8 +14,8 @@ class Tile int m_x; int m_y; - void render(Engine& engine, int scale); - TileId getType() const; + void render(Engine& engine, int scale); + TileId getType() const; }; } // namespace advanced_wars \ No newline at end of file diff --git a/src/game/Unit.hpp b/src/game/Unit.hpp index 249783c..2202f93 100644 --- a/src/game/Unit.hpp +++ b/src/game/Unit.hpp @@ -3,69 +3,16 @@ #include "Engine.hpp" #include "Weapon.hpp" +#include <SDL_events.h> #include <optional> #include <unordered_map> +#include <vector> namespace advanced_wars { - +class Engine; class Config; -enum class UnitFaction -{ - URED = 0, - UBLUE = 1, - UGREEN = 2, - UYELLOW = 3, - UPURPLE = 4, -}; - -enum class UnitId -{ - INFANTERY = 0, - MECHANIZED_INFANTERY = 1, - RECON = 2, - MEDIUM_TANK = 3, - HEAVY_TANK = 4, - NEO_TANK = 5, - APC = 6, - ANTI_AIR_TANK = 7, - ARTILLERY = 8, - ROCKET_ARTILLERY = 9, - ANTI_AIR_MISSILE_LAUNCHER = 10, - FIGHTER = 11, - BOMBER = 12, - BATTLE_HELICOPTER = 13, - TRANSPORT_HELICOPTER = 14, - BATTLESHIP = 15, - CRUISER = 16, - LANDER = 17, - SUBMARINE = 18, - - FIRST = INFANTERY, - LAST = SUBMARINE -}; - -enum class UnitState -{ - IDLE = 0, - UNAVAILABLE = 1, - MOVEMENTLEFT = 2, - MOVEMENTRIGHT = 3, - MOVEMENTDOWN = 4, - MOVEMENTUP = 5, -}; - -enum class MovementType -{ - FOOT = 0, - WHEELED = 1, - TREAD = 2, - AIR = 3, - SEA = 4, - LANDER = 5, -}; - using MatchupTable = std::unordered_map<UnitId, std::unordered_map<UnitId, int>>; class Unit @@ -91,7 +38,7 @@ class Unit * (Currently assumes this triggers certain game events, though not explicitly detailed * here.) */ - ~Unit(){}; + ~Unit() {}; /** * Renders the unit on the game screen. -- GitLab From 58a847207d008053567330e3d6b412d81e52d21c Mon Sep 17 00:00:00 2001 From: Frederik <frederik@prasch.de> Date: Thu, 6 Feb 2025 01:25:26 +0100 Subject: [PATCH 09/10] Fix EventHandler for Movement and Attack range that got broken by merge --- src/game/Engine.cpp | 5 ++ src/game/Level.cpp | 129 +++++++++++++++++++++++++++++++++----------- 2 files changed, 103 insertions(+), 31 deletions(-) diff --git a/src/game/Engine.cpp b/src/game/Engine.cpp index b1c7039..83f03f5 100644 --- a/src/game/Engine.cpp +++ b/src/game/Engine.cpp @@ -120,6 +120,11 @@ Spritesheet* Engine::getSpritesheet() return m_spritesheet.value(); } +Config& Engine::getUnitConfig() +{ + return m_unitConfig; +} + SDL_Renderer* Engine::renderer() { return this->m_SDLRenderer; diff --git a/src/game/Level.cpp b/src/game/Level.cpp index d08f68e..44f36ee 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -371,7 +371,8 @@ Effect Level::removeEffect(int id) return value; } -void Level::handleRecruitingEvent(Engine& engine, SDL_Event& event) { +void Level::handleRecruitingEvent(Engine& engine, SDL_Event& event) +{ switch (event.type) { @@ -384,21 +385,25 @@ void Level::handleRecruitingEvent(Engine& engine, SDL_Event& event) { { m_recruitingMenu.handleEvent(engine, event); } - if (event.key.keysym.sym == SDLK_RETURN) + if (event.key.keysym.sym == SDLK_RETURN) { - Building& b = m_buildings.at(m_selectedBuilding); - UnitFaction u_faction = static_cast<UnitFaction> (b.m_faction); - UnitId unit_id = m_recruitingMenu.getSelectedOption(); + Building& b = m_buildings.at(m_selectedBuilding); + UnitFaction u_faction = static_cast<UnitFaction>(b.m_faction); + UnitId unit_id = m_recruitingMenu.getSelectedOption(); - if(b.check_money(500)) { - if(b.check_spawn(m_units)){ - addUnit(Unit(b.m_x, b.m_y, u_faction, unit_id, UnitState::IDLE)); + if (b.check_money(500)) + { + if (b.check_spawn(m_units)) + { + addUnit(Unit( + b.m_x, b.m_y, u_faction, unit_id, UnitState::IDLE, engine.getUnitConfig())); m_state = LevelState::SELECTING_STATE; m_selectedBuilding = -1; } } } -}} + } +} //*******************helper functions for event Handling************************************* @@ -415,17 +420,27 @@ void Level::handleAttack(std::pair<int, int> tilePos) Unit& attacking = m_units.at(m_selectedUnit); Unit& defending = m_units.at(targetedUnit); - attacking.attack(defending); - if (attacking.m_health <= 0) + + if (m_attackableUnitIds.find(targetedUnit) != m_attackableUnitIds.end()) { - removeUnit(m_selectedUnit); + attacking.attack(defending); + if (attacking.m_health <= 0) + { + removeUnit(m_selectedUnit); + } + if (defending.m_health <= 0) + { + removeUnit(targetedUnit); + } + m_selectedUnit = -1; + m_showAttackableTiles = false; + m_showReachableTiles = false; + m_state = LevelState::SELECTING_STATE; } - if (defending.m_health <= 0) + else { - removeUnit(targetedUnit); + std::cout << "No target in range clicked!" << std::endl; } - m_selectedUnit = -1; - m_state = LevelState::SELECTING_STATE; } else { @@ -444,9 +459,30 @@ void Level::handleMovement(std::pair<int, int> tilePos) return; } } - m_units.at(m_selectedUnit).updatePosition(tilePos.first, tilePos.second); - m_selectedUnit = -1; - m_state = LevelState::SELECTING_STATE; + + bool isReachable = false; + + for (auto& pos : m_reachableTiles) + { + if (pos == tilePos) + { + isReachable = true; + break; + } + } + + if (isReachable) + { + m_units.at(m_selectedUnit).updatePosition(tilePos.first, tilePos.second); + m_selectedUnit = -1; + m_showAttackableTiles = false; + m_showReachableTiles = false; + m_state = LevelState::SELECTING_STATE; + } + else + { + std::cout << "Unglültige Bewegunsposition!" << std::endl; + } } void Level::handlePositionMarker(Engine& engine, SDL_Event& event) @@ -486,6 +522,40 @@ void Level::handleSelectingEvents(Engine& engine, SDL_Event& event) (tilePos.second * 16 + 15) * RENDERING_SCALE); if (m_selectedUnit >= 0) { + m_reachableTiles = calculateMovementRange(m_units.at(m_selectedUnit)); + m_units.at(m_selectedUnit).on_left_click(event); + m_showReachableTiles = true; + + std::vector<Unit*> allUnits; + + for (auto& [id, unit] : m_units) + { + allUnits.push_back(&unit); + } + + std::vector<Unit*> attackableTargets = + m_units.at(m_selectedUnit).getUnitsInRangeWithDamagePotential(allUnits); + + m_attackableTiles.clear(); + m_showAttackableTiles = true; + m_attackableUnitIds.clear(); + + for (Unit* target : attackableTargets) + { + // Füge die Position jedes angreifbaren Ziels hinzu + m_attackableTiles.emplace_back(target->m_x, target->m_y); + + // Angreifbaren Einheits-ID setzen + for (auto& [id, unit] : m_units) + { + if (&unit == target) + { + m_attackableUnitIds.insert(id); + break; + } + } + } + m_contextMenu.setOptions({"Move", "Attack", "Info", "Wait"}); } else @@ -519,6 +589,8 @@ void Level::handleSelectingEvents(Engine& engine, SDL_Event& event) } else { + m_showReachableTiles = false; + m_showAttackableTiles = false; m_state = LevelState::SELECTING_STATE; } } @@ -537,6 +609,8 @@ void Level::handleMenuActiveEvents(Engine& engine, SDL_Event& event) m_selectedUnit = -1; m_selectedBuilding = -1; m_state = LevelState::SELECTING_STATE; + m_showAttackableTiles = false; + m_showReachableTiles = false; } if (event.key.keysym.sym == SDLK_UP || event.key.keysym.sym == SDLK_DOWN) { @@ -580,18 +654,11 @@ void Level::handleMenuActiveEvents(Engine& engine, SDL_Event& event) m_recruitingMenu.update( (tilePos.first * 16 + 15) * RENDERING_SCALE, (tilePos.second * 16 + 15) * RENDERING_SCALE); - m_recruitingMenu.setOptions({ - UnitId::INFANTERY, - UnitId::MECHANIZED_INFANTERY, - UnitId::RECON, - UnitId::APC, - UnitId::ARTILLERY, - UnitId::ANTI_AIR_TANK, - UnitId::ANTI_AIR_MISSILE_LAUNCHER, - UnitId::ROCKET_ARTILLERY, - UnitId::MEDIUM_TANK, - UnitId::NEO_TANK, - UnitId::HEAVY_TANK}); + m_recruitingMenu.setOptions( + {UnitId::INFANTERY, UnitId::MECHANIZED_INFANTERY, UnitId::RECON, UnitId::APC, + UnitId::ARTILLERY, UnitId::ANTI_AIR_TANK, UnitId::ANTI_AIR_MISSILE_LAUNCHER, + UnitId::ROCKET_ARTILLERY, UnitId::MEDIUM_TANK, UnitId::NEO_TANK, + UnitId::HEAVY_TANK}); std::cout << "no training here" << std::endl; } } -- GitLab From 0291139d27babe00618eb30a8be98b51964399f0 Mon Sep 17 00:00:00 2001 From: Frederik <frederik@prasch.de> Date: Thu, 6 Feb 2025 01:27:15 +0100 Subject: [PATCH 10/10] Fix Inverse cliff corners having wrong move cost --- src/game/Level.hpp | 67 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/game/Level.hpp b/src/game/Level.hpp index f3f25a1..c4d0628 100644 --- a/src/game/Level.hpp +++ b/src/game/Level.hpp @@ -22,39 +22,40 @@ namespace advanced_wars const int NUM_TILE_IDS = 30; // Aktualisieren, falls weitere IDs hinzugefügt werden const int NUM_MOVEMENT_TYPES = 6; - -const std::array<std::array<int, NUM_MOVEMENT_TYPES>, NUM_TILE_IDS> moveCostTable = {{ - // FOOT, WHEELED, TREAD, AIR, SEA, LANDER - { 1, 2, 1, 1, 999, 999 }, // PLAIN - {999, 999, 999, 1, 1, 1 }, // WATER - { 1, 3, 2, 1, 999, 999 }, // FOREST - { 2, 999, 999, 1, 999, 999 }, // MOUNTAIN - { 1, 1, 1, 1, 999, 999 }, // BRIDGE_HORIZONTAL - { 1, 1, 1, 1, 999, 999 }, // STREET_HORIZONTAL - { 1, 1, 1, 1, 999, 999 }, // STREET_VERTICAL - { 1, 1, 1, 1, 999, 999 }, // STREET_CROSSING - { 1, 1, 1, 1, 999, 999 }, // STREET_JUNCTION_RIGHT - { 1, 1, 1, 1, 999, 999 }, // STREET_JUNCTION_LEFT - { 1, 1, 1, 1, 999, 999 }, // STREET_JUNCTION_DOWN - { 1, 1, 1, 1, 999, 999 }, // STREET_JUNCTION_UP - { 1, 1, 1, 1, 999, 999 }, // STREET_CORNER_TOP_LEFT - { 1, 1, 1, 1, 999, 999 }, // STREET_CORNER_TOP_RIGHT - { 1, 1, 1, 1, 999, 999 }, // STREET_CORNER_BOTTOM_LEFT - { 1, 1, 1, 1, 999, 999 }, // STREET_CORNER_BOTTOM_RIGHT - {999, 999, 999, 1, 2, 2 }, // RIFF - {999, 999, 999, 1, 1, 1 }, // CLIFF_TOP - {999, 999, 999, 1, 1, 1 }, // CLIFF_BOTTOM - {999, 999, 999, 1, 1, 1 }, // CLIFF_LEFT - {999, 999, 999, 1, 1, 1 }, // CLIFF_RIGHT - {999, 999, 999, 1, 1, 1 }, // CLIFF_CORNER_TOP_LEFT - {999, 999, 999, 1, 1, 1 }, // CLIFF_CORNER_TOP_RIGHT - {999, 999, 999, 1, 1, 1 }, // CLIFF_CORNER_BOTTOM_LEFT - {999, 999, 999, 1, 1, 1 }, // CLIFF_CORNER_BOTTOM_RIGHT - { 1, 2, 1, 1, 999, 999 }, // CLIFF_INVERSE_CORNER_TOP_LEFT - { 1, 2, 1, 1, 999, 999 }, // CLIFF_INVERSE_CORNER_TOP_RIGHT - { 1, 2, 1, 1, 999, 999 }, // CLIFF_INVERSE_CORNER_BOTTOM_LEFT - { 1, 2, 1, 1, 999, 999 }, // CLIFF_INVERSE_CORNER_BOTTOM_RIGHT -}}; +const std::array<std::array<int, NUM_MOVEMENT_TYPES>, NUM_TILE_IDS> moveCostTable = { + { + // FOOT, WHEELED, TREAD, AIR, SEA, LANDER + {1, 2, 1, 1, 999, 999}, // PLAIN + {999, 999, 999, 1, 1, 1}, // WATER + {1, 3, 2, 1, 999, 999}, // FOREST + {2, 999, 999, 1, 999, 999}, // MOUNTAIN + {1, 1, 1, 1, 999, 999}, // BRIDGE_HORIZONTAL + {1, 1, 1, 1, 999, 999}, // STREET_HORIZONTAL + {1, 1, 1, 1, 999, 999}, // STREET_VERTICAL + {1, 1, 1, 1, 999, 999}, // STREET_CROSSING + {1, 1, 1, 1, 999, 999}, // STREET_JUNCTION_RIGHT + {1, 1, 1, 1, 999, 999}, // STREET_JUNCTION_LEFT + {1, 1, 1, 1, 999, 999}, // STREET_JUNCTION_DOWN + {1, 1, 1, 1, 999, 999}, // STREET_JUNCTION_UP + {1, 1, 1, 1, 999, 999}, // STREET_CORNER_TOP_LEFT + {1, 1, 1, 1, 999, 999}, // STREET_CORNER_TOP_RIGHT + {1, 1, 1, 1, 999, 999}, // STREET_CORNER_BOTTOM_LEFT + {1, 1, 1, 1, 999, 999}, // STREET_CORNER_BOTTOM_RIGHT + {999, 999, 999, 1, 2, 2}, // RIFF + {999, 999, 999, 1, 1, 1}, // CLIFF_TOP + {999, 999, 999, 1, 1, 1}, // CLIFF_BOTTOM + {999, 999, 999, 1, 1, 1}, // CLIFF_LEFT + {999, 999, 999, 1, 1, 1}, // CLIFF_RIGHT + {999, 999, 999, 1, 1, 1}, // CLIFF_CORNER_TOP_LEFT + {999, 999, 999, 1, 1, 1}, // CLIFF_CORNER_TOP_RIGHT + {999, 999, 999, 1, 1, 1}, // CLIFF_CORNER_BOTTOM_LEFT + {999, 999, 999, 1, 1, 1}, // CLIFF_CORNER_BOTTOM_RIGHT + {999, 999, 999, 1, 1, 1}, // CLIFF_INVERSE_CORNER_TOP_LEFT + {999, 999, 999, 1, 1, 1}, // CLIFF_INVERSE_CORNER_TOP_RIGHT + {999, 999, 999, 1, 1, 1}, // CLIFF_INVERSE_CORNER_BOTTOM_LEFT + {999, 999, 999, 1, 1, 1}, // CLIFF_INVERSE_CORNER_BOTTOM_RIGHT + } +}; enum class LevelState { -- GitLab