diff --git a/src/building.cpp b/src/building.cpp index 02c0a98fd0726d9abb2d30329d4a86f886c1b648..c6b47dc815c2f2642ddd11aa13766ca36419ed09 100644 --- a/src/building.cpp +++ b/src/building.cpp @@ -5,7 +5,7 @@ namespace advanced_wars { Building::Building(int x, int y, BuildingId id, BuildingFaction faction) - : x(x), y(y), id(id), faction(faction) {}; + : x(x), y(y), id(id), faction(faction){}; void Building::render(Engine* engine, int scale) { diff --git a/src/effect.cpp b/src/effect.cpp index 2636b5dbeee5de6d995ae4afabc353e05d28eb32..f3ab594394084dd214a95a6078715c81478a447c 100644 --- a/src/effect.cpp +++ b/src/effect.cpp @@ -6,9 +6,9 @@ namespace advanced_wars { Effect::Effect(int x, int y, EffectId id, bool repeat) - : x(x), y(y), id(id), repeat(repeat), start(0) { + : x(x), y(y), id(id), repeat(repeat), start(0){ - }; + }; void Effect::render(Engine* engine, int scale) { diff --git a/src/level.cpp b/src/level.cpp index 9591a4a70e796d0b4d2bef3892bb4708682471b2..927ca47a5c15490203634e5c098e158a165274e5 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -7,6 +7,7 @@ #include "ui/pausemenu.hpp" #include "unit.hpp" #include <SDL.h> +#include <algorithm> #include <iostream> #include <string> @@ -43,14 +44,165 @@ Level::Level( } }; +const int RENDERING_SCALE = 3; + +bool Level::click_check_left(int tileX, int tileY) +{ + + if (selectUnit(tileX, tileY)) + { + return true; + } + + if (selectBuilding(tileX, tileY)) + { + return true; + } + + return false; +} + +bool Level::click_check_right(int tileX, int tileY) +{ + + if (target_unit(tileX, tileY)) + { + return true; + } + + return false; +} + +bool Level::selectUnit(int tileX, int tileY) +{ + + // std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; + for (auto& [id, unit] : units) + { + + if (unit.x == tileX && unit.y == tileY) + { + // std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; + + selectedUnit = id; + return true; + } + } + + return false; +} + +bool Level::target_unit(int tileX, int tileY) +{ + + // std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; + for (auto& [id, unit] : units) + { + + if (unit.x == tileX && unit.y == tileY) + { + // std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; + + targetedUnit = id; + return true; + } + } + + return false; +} + +bool Level::selectBuilding(int tileX, int tileY) +{ + + for (auto& [id, building] : buildings) + { + + if (building.x == tileX && building.y == tileY) + { + // std::cout << "X:" << unit.x << "Y:" << unit.y << std::endl; + selectedBuilding = id; + return true; + } + } + return false; +} + +void Level::handleEvent(Engine& engine, SDL_Event& event) +{ + + switch (event.type) + { + case SDL_MOUSEBUTTONDOWN: + + if (event.button.button == SDL_BUTTON_LEFT) + { + + int tileX = event.button.x / (16 * RENDERING_SCALE); + int tileY = event.button.y / (16 * RENDERING_SCALE); + + if (click_check_left(tileX, tileY)) + { + + if (selectedUnit > -1) + { + units.at(selectedUnit).on_left_click(event); + } + + if (selectedBuilding > -1) + { + // building stuff + } + } + else + { + + std::cout << "Neither building nor unit clicked!" << std::endl; + selectedUnit = -1; + selectedBuilding = -1; + } + } + else if (event.button.button == SDL_BUTTON_RIGHT) + { + + if (selectedUnit > -1) + { + + int tileX = event.button.x / (16 * RENDERING_SCALE); + int tileY = event.button.y / (16 * RENDERING_SCALE); + + if (click_check_right(tileX, tileY)) + { + + units.at(selectedUnit).attack(&(units.at(targetedUnit))); + + if (units.at(selectedUnit).health <= 0) + { + remove_unit(selectedUnit); + } + } + else + { + + units.at(selectedUnit).update_position(tileX, tileY); + } + } + else + { + + std::cout << "No unit selected! " << std::endl; + } + } + } +} + void Level::render(Engine* engine) { - const int RENDERING_SCALE = 3; // Iterate over all events while (!engine->events().empty()) { - handleEvent(engine, engine->events().at(0)); + // handleEvent(engine, engine->events().at(0)); + handleEvent(*engine, engine->events().at(0)); engine->events().pop_front(); } diff --git a/src/level.hpp b/src/level.hpp index 422da0eb082cc7e8c9b64f425001f0df01a8ad21..07501485dffd2ac452ad84816bc272beeebe0722 100644 --- a/src/level.hpp +++ b/src/level.hpp @@ -41,6 +41,8 @@ class Level : public Scene Effect remove_effect(int id); + void handleEvent(Engine& engine, SDL_Event& event); + private: std::string name; int width; @@ -50,6 +52,15 @@ class Level : public Scene std::unordered_map<int, Building> buildings; std::unordered_map<int, Unit> units; std::unordered_map<int, Effect> effects; + int selectedUnit; + int targetedUnit; + int selectedBuilding; + bool selectUnit(int tileX, int tileY); + bool target_unit(int tileX, int tileY); + bool selectBuilding(int tileX, int tileY); + + bool click_check_left(int mouseX, int mouseY); + bool click_check_right(int mouseX, int mouseY); ContextMenu context_menu; bool context_menu_active; diff --git a/src/tile.cpp b/src/tile.cpp index 64ce524c5449fe749bbe7ea8d091e5e21ec42c5a..525ee8385468b07df1f3ebcddf57be84e523b7e6 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -5,10 +5,7 @@ namespace advanced_wars { -Tile::Tile(TileId id, int x, int y) - : id(id), x(x), y(y) { - - }; +Tile::Tile(TileId id, int x, int y) : id(id), x(x), y(y) {} void Tile::render(Engine* engine, int scale) { diff --git a/src/unit.cpp b/src/unit.cpp index 7d6b3e657e7a2cdfaac4529f8789f79ccced5f67..4ece9fb5a13f8ca81b247be886cbc35f50d7e26a 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -1,12 +1,22 @@ #include "unit.hpp" +#include <iostream> namespace advanced_wars { Unit::Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state) - : x(x), y(y), faction(faction), id(id), state(state) { - - }; + : x(x), y(y), faction(faction), id(id), state(state), max_health(100) +{ + // das ist nur für Testzwecke + if (id == UnitId::INFANTERY) + { + secondary_weapon = Weapon( + "Machine-Gun", { + {UnitId::INFANTERY, 55} + }); + } + health = max_health; +} void Unit::render(Engine* engine, int scale) { @@ -69,4 +79,141 @@ void Unit::render(Engine* engine, int scale) } } +void Unit::attack(Unit* enemy) +{ + // Angenommen, primary_weapon und secondary_weapon wurden bereits korrekt + // initialisiert + auto primary_weapon_damage_it = primary_weapon.damage.find(enemy->id); + auto secondary_weapon_damage_it = secondary_weapon.damage.find(enemy->id); + + int attacker_damage_value = 0; + + // Die Waffe mit dem höchsten Schaden wählen + if (secondary_weapon_damage_it != secondary_weapon.damage.end()) + { + attacker_damage_value = secondary_weapon_damage_it->second; + } + + if (primary_weapon_damage_it != primary_weapon.damage.end()) + { + if (primary_weapon_damage_it->second > attacker_damage_value) + { + // Munitionsabzug sollte hier erfolgen, falls zutreffend + attacker_damage_value = primary_weapon_damage_it->second; + } + } + + if (attacker_damage_value == 0) + { + std::cout << "No damage value found for attack from unit " << static_cast<int>(id) + << " against unit " << static_cast<int>(enemy->id) << std::endl; + } + else + { + int off_damage = attacker_damage_value * (static_cast<float>(health) / max_health); + enemy->health -= off_damage; + enemy->health = std::max( + 0, + enemy->health); // Sicherstellen, dass die Gesundheit nicht negativ wird + std::cout << "Enemy health after attack: " << enemy->health << std::endl; + + // Prüfen, ob der Gegner noch am Leben ist um zurückzuschlagen + if (enemy->health > 0) + { + // Weapon tables for the defender + auto defender_primary_weapon_damage_it = enemy->primary_weapon.damage.find(id); + auto defender_secondary_weapon_damage_it = enemy->secondary_weapon.damage.find(id); + + int defender_damage_value = 0; // Declare outside for later use + + // Determine the damage value for the defender + if (defender_secondary_weapon_damage_it != enemy->secondary_weapon.damage.end()) + { + defender_damage_value = defender_secondary_weapon_damage_it->second; + } + + if (defender_primary_weapon_damage_it != enemy->primary_weapon.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->health) / enemy->max_health); + this->health -= def_damage; + this->health = std::max(0, this->health); // Safeguard against negative health + std::cout << "Ally health after retaliation: " << this->health << std::endl; + } + } + } +} + +void Unit::update_position(int posX, int posY) +{ + calc_state(posX, posY); + + this->x = posX; + this->y = posY; +} + +void Unit::calc_state(int posX, int posY) +{ + int deltaX = this->x - posX; + int deltaY = this->y - posY; + + if (deltaX == 0 && deltaY == 0) + { + // Unit is already at the target position + return; + } + + if (abs(deltaX) >= abs(deltaY)) + { + if (deltaX > 0) + { + this->state = advanced_wars::UnitState::MOVEMENTLEFT; + } + else + { + this->state = advanced_wars::UnitState::MOVEMENTRIGHT; + } + } + else + { + if (deltaY > 0) + { + this->state = advanced_wars::UnitState::MOVEMENTUP; + } + else + { + this->state = advanced_wars::UnitState::MOVEMENTDOWN; + } + } +} + +void Unit::on_left_click(SDL_Event event) +{ + + std::cout << "Left-button pressed on unit: " << this->health << std::endl; +} + +bool Unit::inRange(Unit* enemy) +{ + if (this->x == enemy->x) + { + return abs(this->y - enemy->y) <= this->range; + } + else if (this->y == enemy->y) + { + return abs(this->x - enemy->x) <= this->range; + } + return false; +} + } // namespace advanced_wars \ No newline at end of file diff --git a/src/unit.hpp b/src/unit.hpp index 8d01704f85cb5419983fdf13e4bf179bc908c1a9..489dd3c45a606ce7d21bc44c7cd26ec3175ea99a 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -1,6 +1,9 @@ #pragma once #include "engine.hpp" +#include "weapon.hpp" +#include <optional> +#include <unordered_map> namespace advanced_wars { @@ -57,19 +60,80 @@ enum class MovementType LANDER = 5, }; +using MatchupTable = std::unordered_map<UnitId, std::unordered_map<UnitId, int>>; + class Unit { public: + int x; + int y; + int health; // health equals max_health at construction + Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state); + ~Unit() + { + // Assuming that the destruktion of a unit triggers events + } 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 + */ + + 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 + */ + + void attack(Unit* enemy); + + /* + @params Takes the desired position of the unit and updates its values + This will teleport the unit, there is no smooth transition between tiles + */ + void update_position(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 + */ + void calculate_movement(); + + void calc_state(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 + */ + void on_left_click(SDL_Event event); + private: - int x; - int y; UnitFaction faction; UnitId id; UnitState state; + + int max_health; // max_health required for damage_scaling + int range; + int fuel; + int max_fuel; + + bool has_moved; + bool has_attacked; + bool is_selected; + bool is_targeted; + Weapon secondary_weapon; + Weapon primary_weapon; + + int ammo; }; } // namespace advanced_wars \ No newline at end of file diff --git a/src/weapon.cpp b/src/weapon.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e324130e26681f5cef3d0334e60abb90086c4aa7 --- /dev/null +++ b/src/weapon.cpp @@ -0,0 +1,29 @@ +#include "weapon.hpp" + +namespace advanced_wars +{ +Weapon::Weapon() : name(""), damage() {} + +Weapon::Weapon(const std::string& weaponName, const std::unordered_map<UnitId, int>& damageValues) + : name(weaponName), damage(damageValues) +{ +} + +// Funktion zum Hinzufügen von Schadenswerten +void Weapon::addDamageValue(UnitId unitId, int value) +{ + damage[unitId] = value; +} + +// Funktion zum Abrufen eines Schadenswertes +int Weapon::getDamageValue(UnitId unitId) const +{ + auto it = damage.find(unitId); + if (it != damage.end()) + { + return it->second; + } + return 0; // oder ein Fehlerwert +} + +} // namespace advanced_wars \ No newline at end of file diff --git a/src/weapon.hpp b/src/weapon.hpp new file mode 100644 index 0000000000000000000000000000000000000000..028eef1a67f30ea825b2a5c2b633d48af2c20216 --- /dev/null +++ b/src/weapon.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <string> +#include <unordered_map> + +namespace advanced_wars +{ + +enum class UnitId; + +class Weapon +{ + public: + // Konstruktoren + Weapon(); + Weapon(const std::string& weaponName, const std::unordered_map<UnitId, int>& damageValues); + + // Methode, um einen Schadenswert hinzuzufügen + void addDamageValue(UnitId unitId, int value); + + // Methode, um einen Schadenswert abzurufen + int getDamageValue(UnitId unitId) const; + + // Name der Waffe + std::string name; + + // Schadenstabelle + std::unordered_map<UnitId, int> damage; +}; + +} // namespace advanced_wars