From b8b5e5d90cbb4a617d2a244757c077654a69a73e Mon Sep 17 00:00:00 2001 From: fdai7375 <david.maul@informatik.hs-fulda.de> Date: Mon, 27 Jan 2025 01:16:02 +0100 Subject: [PATCH 01/30] experimental changes for units and weapons --- src/unit.hpp | 17 +++++++++++++++++ src/weapon.cpp | 3 +++ src/weapon.hpp | 21 +++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 src/weapon.cpp create mode 100644 src/weapon.hpp diff --git a/src/unit.hpp b/src/unit.hpp index 20877ba..878f925 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -1,6 +1,8 @@ #pragma once #include "engine.hpp" +#include "weapon.hpp" +#include <optional> namespace advanced_wars { @@ -61,9 +63,24 @@ public: private: int x; int y; + UnitFaction faction; UnitId id; UnitState state; + + int health; + int range; + int fuel; + int max_fuel; + + bool has_moved; + bool has_attacked; + + // Primary weapon ammo + int ammo; + + std::optional<Weapon> primary; + std::optional<Weapon> secondary; }; } // namespace advanced_wars \ No newline at end of file diff --git a/src/weapon.cpp b/src/weapon.cpp new file mode 100644 index 0000000..650e950 --- /dev/null +++ b/src/weapon.cpp @@ -0,0 +1,3 @@ +#include "weapon.hpp" + +namespace advanced_wars {} \ No newline at end of file diff --git a/src/weapon.hpp b/src/weapon.hpp new file mode 100644 index 0000000..753e674 --- /dev/null +++ b/src/weapon.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "unit.hpp" +#include <unordered_map> + +namespace advanced_wars { + +// Forward Declaration +enum class UnitId; + +class Weapon { +public: + // Ranges + int min_range; + int max_range; + + // Damage + std::unordered_map<UnitId, int> damage; +}; + +} // namespace advanced_wars \ No newline at end of file -- GitLab From 21ca6c12f1191ae9ae3a6f09df0a660238e39ec2 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Mon, 27 Jan 2025 13:23:50 +0100 Subject: [PATCH 02/30] Update unit.hpp with required methods --- src/unit.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/unit.hpp b/src/unit.hpp index 878f925..31d690e 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -60,6 +60,29 @@ public: void render(Engine &engine, int scale); + + /* + 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 init_combat(Unit &defender); + + + /* + @params Takes the desired position of the unit and updates its values + */ + 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(); + private: int x; int y; -- GitLab From de1bea635ea9f952f838650574aa63e0c8beb104 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Mon, 27 Jan 2025 15:44:16 +0100 Subject: [PATCH 03/30] Add nested unordered_map for damageMatrix --- src/unit.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/unit.hpp b/src/unit.hpp index 31d690e..bdfb037 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -54,6 +54,9 @@ enum class MovementType { LANDER = 5, }; +//Fill the MatchupTabel +using MatchupTabel = std::unordered_map<u_int8_t, std::unordered_map<u_int8_t, int>>; + class Unit { public: Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state); @@ -68,7 +71,7 @@ public: Will Update the health for both units Attacker deals damage to the defender first */ - void init_combat(Unit &defender); + void attack(Unit &enemy); /* @@ -83,6 +86,8 @@ public: */ void calculate_movement(); + + private: int x; int y; -- GitLab From 393dc7334a73b7da969c127722e10e9fc3c4e674 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Mon, 27 Jan 2025 15:49:55 +0100 Subject: [PATCH 04/30] add first rendition of combat system --- src/unit.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/unit.cpp b/src/unit.cpp index cb0b87b..b5df46e 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -62,4 +62,22 @@ void Unit::render(Engine &engine, int scale) { } } +//Placeholder +MatchupTabel damageMatrix = { + { UnitId::INFANTRY, {{ UnitId::INFANTRY, 50 }, { UnitId::TANK, 5 }} }, + { UnitId::TANK, {{ UnitId::INFANTRY, 90 }, { UnitId::TANK, 50 }} } +}; +void Unit::attack(Unit &enemy) { + + + //Start Attack: choose the appropriate weapon: + int offDamage = damageMatrix[this->id][enemy->id] * ((this->health)/(this->max_health)); + int defDamage = damageMatrix[this->id][enemy->id] * ((enemy->health)/(enemy->max_health)); + + enemy->health = enemy->health - offDamage; + if(enemy->health > 0) { + this->health = this->health - defDamage; + } +} + } // namespace advanced_wars \ No newline at end of file -- GitLab From 2d37a5c81a23d49bb28c5274591cac17821ecbee Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Mon, 27 Jan 2025 16:06:34 +0100 Subject: [PATCH 05/30] Add comments to signal future progress --- src/unit.cpp | 15 ++++++++++++--- src/unit.hpp | 9 +++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index b5df46e..983df79 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -4,7 +4,7 @@ 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) { - + health = max_health; }; void Unit::render(Engine &engine, int scale) { @@ -64,11 +64,15 @@ void Unit::render(Engine &engine, int scale) { //Placeholder MatchupTabel damageMatrix = { - { UnitId::INFANTRY, {{ UnitId::INFANTRY, 50 }, { UnitId::TANK, 5 }} }, - { UnitId::TANK, {{ UnitId::INFANTRY, 90 }, { UnitId::TANK, 50 }} } + { UnitId::INFANTRY, {{ UnitId::INFANTRY, 50 }, { UnitId::MEDIUM_TANK, 5 }} }, + { UnitId::MEDIUM_TANK, {{ UnitId::INFANTRY, 90 }, { UnitId::MEDIUM_TANK, 50 }} } }; + void Unit::attack(Unit &enemy) { + //checks to be run: + //is unit in range? + //has unit not attacked this turn? //Start Attack: choose the appropriate weapon: int offDamage = damageMatrix[this->id][enemy->id] * ((this->health)/(this->max_health)); @@ -77,6 +81,11 @@ void Unit::attack(Unit &enemy) { enemy->health = enemy->health - offDamage; if(enemy->health > 0) { this->health = this->health - defDamage; + if(this->health <= 0) { + this->~Unit(); + } + } else { + enemy->~Unit(); } } diff --git a/src/unit.hpp b/src/unit.hpp index bdfb037..a980b2a 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -60,10 +60,12 @@ using MatchupTabel = std::unordered_map<u_int8_t, std::unordered_map<u_int8_t, i class Unit { public: 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); - /* The attacker will move towards the defender and thus initiate combat @params Takes a reference to the defender @@ -86,8 +88,6 @@ public: */ void calculate_movement(); - - private: int x; int y; @@ -96,7 +96,8 @@ private: UnitId id; UnitState state; - int health; + int health; //health equals max_health at construction + int max_health; // max_health required for damage_scaling int range; int fuel; int max_fuel; -- GitLab From 952853aa32f61ec193f134dfd9d567606d43aaeb Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Mon, 27 Jan 2025 18:24:21 +0100 Subject: [PATCH 06/30] Add function to check for Range from allied to enemy unit(s) --- src/unit.cpp | 18 ++++++++++++++++-- src/unit.hpp | 8 ++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index 983df79..17ea40b 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -70,9 +70,14 @@ MatchupTabel damageMatrix = { void Unit::attack(Unit &enemy) { - //checks to be run: - //is unit in range? //has unit not attacked this turn? + if(!inRange(Unit &enemy)) { + //Render enemies as not in Range + } + + if (this->has_attacked) { + //display the unit as not able to attack (maybe greyscale?) + } //Start Attack: choose the appropriate weapon: int offDamage = damageMatrix[this->id][enemy->id] * ((this->health)/(this->max_health)); @@ -89,4 +94,13 @@ void Unit::attack(Unit &enemy) { } } +void 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 a980b2a..adc11e8 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -66,6 +66,14 @@ public: 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 -- GitLab From 38a8e6eaa87e3e8cbd43749b8979f86648a7b373 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Mon, 27 Jan 2025 19:52:55 +0100 Subject: [PATCH 07/30] Start work on reading the XML-File for the units --- src/unit.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++-------- src/unit.hpp | 7 ++++++- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index 17ea40b..6f9932c 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -1,4 +1,5 @@ #include "unit.hpp" +#include <tinyxml2.h> namespace advanced_wars { @@ -62,17 +63,11 @@ void Unit::render(Engine &engine, int scale) { } } -//Placeholder -MatchupTabel damageMatrix = { - { UnitId::INFANTRY, {{ UnitId::INFANTRY, 50 }, { UnitId::MEDIUM_TANK, 5 }} }, - { UnitId::MEDIUM_TANK, {{ UnitId::INFANTRY, 90 }, { UnitId::MEDIUM_TANK, 50 }} } -}; - void Unit::attack(Unit &enemy) { //has unit not attacked this turn? - if(!inRange(Unit &enemy)) { - //Render enemies as not in Range + if(!inRange(enemy)) { + //Render enemies as not in Range => use the UNAVAILABLE Texture for that } if (this->has_attacked) { @@ -103,4 +98,44 @@ void Unit::inRange(Unit &enemy) { return false; } +void loadXML(const char* filename) { + + tinyxml2::XMLDocument doc; + if (doc.LoadFile(filename) != tinyxml2::XML_SUCCESS) { + std::cerr << "Fehler beim Laden der XML-Datei!" << std::endl; + return; + } + + MatchupTabel damageMatrix; + tinyxml2::XMLElement* unitElement = doc.FirstChildElement("Units")->FirstChildElement("Unit"); + + //get all Units + while(unitElement) { + + const u_int8_t UnitId = unitElement->Attribute("id"); + + std::unordered_map<u_int8_t, int> attackValues; + tinyxml2::XMLElement* attackElement = unitElement->FirstChildElement("Attack"); + + //get all attack-values + while(attackElement) { + + tinyxml2::XMLElement* attackTypeElement = attackElement->FirstChildElement(); + + while(attackTypeElement) { + UnitId Unit_Id = static_cast<UnitId> attackTypeElement->Name(); //wenn das geht kauf ich maggus sein kochbuch + + int attackValue = attackTypeElement->IntText(); + + attackValues[Unit_Id] = attackValue; + + attackTypeElement = attackTypeElement->NextSiblingElement(); + } + } + + damageMatrix[unitElement][attackValues]; + unitElement = unitElement->NextSiblingElement("Unit"); + } + +} } // namespace advanced_wars \ No newline at end of file diff --git a/src/unit.hpp b/src/unit.hpp index adc11e8..e5ee504 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -71,7 +71,7 @@ public: 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); /* @@ -96,6 +96,11 @@ public: */ void calculate_movement(); + /* + Load the XML and iterate over the entire datastructure + */ +void loadXML(const char* filename); + private: int x; int y; -- GitLab From 7c9372937a05beb7d9186216dc2efb30395a40d3 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Mon, 27 Jan 2025 19:57:46 +0100 Subject: [PATCH 08/30] Add description in unit.hpp --- src/unit.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/unit.hpp b/src/unit.hpp index e5ee504..b67a740 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -98,6 +98,7 @@ public: /* Load the XML and iterate over the entire datastructure + For every Unit, get Unit_Id and create a map for Unit_Id -> {Unit_Id -> damageType} */ void loadXML(const char* filename); -- GitLab From ab45c86e69d4a76af95ad5cee0f8ec2a492e3b3c Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Tue, 28 Jan 2025 11:24:43 +0100 Subject: [PATCH 09/30] Add onClick for units and basic attack initialization --- src/unit.cpp | 68 +++++++++++++++++++++++++++++++++++++++++----------- src/unit.hpp | 4 ++++ 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index 6f9932c..8673d40 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -63,33 +63,73 @@ void Unit::render(Engine &engine, int scale) { } } -void Unit::attack(Unit &enemy) { +void Unit::attack(Unit &ally, Unit &enemy) { - //has unit not attacked this turn? - if(!inRange(enemy)) { - //Render enemies as not in Range => use the UNAVAILABLE Texture for that - } - - if (this->has_attacked) { + if (ally->has_attacked) { //display the unit as not able to attack (maybe greyscale?) } //Start Attack: choose the appropriate weapon: - int offDamage = damageMatrix[this->id][enemy->id] * ((this->health)/(this->max_health)); - int defDamage = damageMatrix[this->id][enemy->id] * ((enemy->health)/(enemy->max_health)); + int offDamage = damageMatrix[ally->id][enemy->id] * ((ally->health)/(ally->max_health)); + int defDamage = damageMatrix[ally->id][enemy->id] * ((enemy->health)/(enemy->max_health)); enemy->health = enemy->health - offDamage; if(enemy->health > 0) { - this->health = this->health - defDamage; - if(this->health <= 0) { - this->~Unit(); + ally->health = ally->health - defDamage; + if(ally->health <= 0) { + ally->~Unit(); } } else { enemy->~Unit(); } } -void Unit::inRange(Unit &enemy) { +void Unit::onClick(SDL_Event event) { + + Unit &defender; + Unit &attacker; + + switch (event.button.button) { + case SDL_BUTTON_LEFT: + + this->is_selected = true; + + for (Unit &unit : units) { + if(inRange(unit)) { + unit.state = UNAVAILABLE; + }; + } + break; + case SDL_BUTTON_RIGHT: + + this->is_targeted = true; + + for (Unit &unit : units) { + if(unit.state = UNAVAILABLE) { + continue; + } + + if(unit.is_selected) { + attacker = &unit; + } + + if(unit.is_targeted) { + defender = &unit; + } + } + + if(attacker && defender) { + attack(attacker, defender); + break; + } else { + stderr("Could not init the attack!"); + break; + } + + } +} + +bool Unit::inRange(Unit &enemy) { if (this->x == enemy.x) { return abs(this->y - enemy.y) <= this->range; } else if (this->y == enemy.y) { @@ -98,7 +138,7 @@ void Unit::inRange(Unit &enemy) { return false; } -void loadXML(const char* filename) { +void Unit::loadXML(const char* filename) { tinyxml2::XMLDocument doc; if (doc.LoadFile(filename) != tinyxml2::XML_SUCCESS) { diff --git a/src/unit.hpp b/src/unit.hpp index b67a740..9ead0b0 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -102,6 +102,8 @@ public: */ void loadXML(const char* filename); +void onClick(SDL_EVENT event); + private: int x; int y; @@ -118,6 +120,8 @@ private: bool has_moved; bool has_attacked; + bool is_selected; + bool is_targeted; // Primary weapon ammo int ammo; -- GitLab From 85ba722558e8f1188881906e3d759bf358fe0835 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Tue, 28 Jan 2025 13:00:38 +0100 Subject: [PATCH 10/30] Update der Unit.cpp --- src/unit.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/unit.cpp b/src/unit.cpp index 8673d40..fa9374b 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -11,6 +11,12 @@ Unit::Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state) void Unit::render(Engine &engine, int scale) { Spritesheet *spritesheet = engine.get_spritesheet(); + while (engine.events not empty) + { + handle_event(events.pop_front()); + + } + int step = engine.get_stage() % spritesheet->get_unit_textures() .at(static_cast<int>(faction)) .at(static_cast<int>(id)) @@ -92,6 +98,7 @@ void Unit::onClick(SDL_Event event) { switch (event.button.button) { case SDL_BUTTON_LEFT: + //we have to re-initialize the unit.state (probably to idle) this->is_selected = true; for (Unit &unit : units) { -- GitLab From 159e0b5f1457fe1ec7c530477d1dc5095f206638 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Tue, 28 Jan 2025 16:28:14 +0100 Subject: [PATCH 11/30] Update with move position and format unit.cpp --- src/unit.cpp | 291 +++++++++++++++++++++++++++++---------------------- src/unit.hpp | 8 +- 2 files changed, 170 insertions(+), 129 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index fa9374b..83875bc 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -1,188 +1,223 @@ #include "unit.hpp" #include <tinyxml2.h> -namespace advanced_wars { +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) { + Unit::Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state) + : x(x), y(y), faction(faction), id(id), state(state) + { health = max_health; - }; - -void Unit::render(Engine &engine, int scale) { - Spritesheet *spritesheet = engine.get_spritesheet(); + }; - while (engine.events not empty) + void Unit::render(Engine &engine, int scale) { - handle_event(events.pop_front()); + Spritesheet *spritesheet = engine.get_spritesheet(); - } + while (engine.events not empty) + { + handle_event(events.pop_front()); + } - int step = engine.get_stage() % spritesheet->get_unit_textures() - .at(static_cast<int>(faction)) - .at(static_cast<int>(id)) - .at(static_cast<int>(state)) - .second; - - if (state == UnitState::IDLE || state == UnitState::UNAVAILABLE) { - - SDL_Rect src; - src.x = step * spritesheet->get_unit_width(); - src.y = 0; - src.w = spritesheet->get_unit_width(); - src.h = spritesheet->get_unit_height(); - - SDL_Rect dst; - dst.x = x * spritesheet->get_unit_width() * scale; - dst.y = y * spritesheet->get_unit_height() * scale; - dst.w = spritesheet->get_unit_width() * scale; - dst.h = spritesheet->get_unit_height() * scale; - - SDL_RenderCopyEx(engine.renderer(), - spritesheet->get_unit_textures() - .at(static_cast<int>(faction)) - .at(static_cast<int>(id)) - .at(static_cast<int>(state)) - .first, - &src, &dst, 0, NULL, SDL_FLIP_NONE); - } else { - // The moving states have a resolution of 24x24 instead of 16x16 and need to - // be handled separately - SDL_Rect src; - src.x = step * spritesheet->get_unit_moving_width(); - src.y = 0; - src.w = spritesheet->get_unit_moving_width(); - src.h = spritesheet->get_unit_moving_height(); - - SDL_Rect dst; - dst.x = ((x * spritesheet->get_unit_width()) - 4) * scale; - dst.y = ((y * spritesheet->get_unit_height()) - 4) * scale; - dst.w = spritesheet->get_unit_moving_width() * scale; - dst.h = spritesheet->get_unit_moving_height() * scale; - - SDL_RenderCopyEx(engine.renderer(), - spritesheet->get_unit_textures() - .at(static_cast<int>(faction)) - .at(static_cast<int>(id)) - .at(static_cast<int>(state)) - .first, - &src, &dst, 0, NULL, SDL_FLIP_NONE); - } -} - -void Unit::attack(Unit &ally, Unit &enemy) { - - if (ally->has_attacked) { - //display the unit as not able to attack (maybe greyscale?) + int step = engine.get_stage() % spritesheet->get_unit_textures() + .at(static_cast<int>(faction)) + .at(static_cast<int>(id)) + .at(static_cast<int>(state)) + .second; + + if (state == UnitState::IDLE || state == UnitState::UNAVAILABLE) + { + + SDL_Rect src; + src.x = step * spritesheet->get_unit_width(); + src.y = 0; + src.w = spritesheet->get_unit_width(); + src.h = spritesheet->get_unit_height(); + + SDL_Rect dst; + dst.x = x * spritesheet->get_unit_width() * scale; + dst.y = y * spritesheet->get_unit_height() * scale; + dst.w = spritesheet->get_unit_width() * scale; + dst.h = spritesheet->get_unit_height() * scale; + + SDL_RenderCopyEx(engine.renderer(), + spritesheet->get_unit_textures() + .at(static_cast<int>(faction)) + .at(static_cast<int>(id)) + .at(static_cast<int>(state)) + .first, + &src, &dst, 0, NULL, SDL_FLIP_NONE); + } + else + { + // The moving states have a resolution of 24x24 instead of 16x16 and need to + // be handled separately + SDL_Rect src; + src.x = step * spritesheet->get_unit_moving_width(); + src.y = 0; + src.w = spritesheet->get_unit_moving_width(); + src.h = spritesheet->get_unit_moving_height(); + + SDL_Rect dst; + dst.x = ((x * spritesheet->get_unit_width()) - 4) * scale; + dst.y = ((y * spritesheet->get_unit_height()) - 4) * scale; + dst.w = spritesheet->get_unit_moving_width() * scale; + dst.h = spritesheet->get_unit_moving_height() * scale; + + SDL_RenderCopyEx(engine.renderer(), + spritesheet->get_unit_textures() + .at(static_cast<int>(faction)) + .at(static_cast<int>(id)) + .at(static_cast<int>(state)) + .first, + &src, &dst, 0, NULL, SDL_FLIP_NONE); + } } - //Start Attack: choose the appropriate weapon: - int offDamage = damageMatrix[ally->id][enemy->id] * ((ally->health)/(ally->max_health)); - int defDamage = damageMatrix[ally->id][enemy->id] * ((enemy->health)/(enemy->max_health)); + void Unit::attack(Unit &ally, Unit &enemy) + { - enemy->health = enemy->health - offDamage; - if(enemy->health > 0) { - ally->health = ally->health - defDamage; - if(ally->health <= 0) { - ally->~Unit(); + if (ally->has_attacked) + { + // display the unit as not able to attack (maybe greyscale?) + } + + // Start Attack: choose the appropriate weapon: + int offDamage = damageMatrix[ally->id][enemy->id] * ((ally->health) / (ally->max_health)); + int defDamage = damageMatrix[ally->id][enemy->id] * ((enemy->health) / (enemy->max_health)); + + enemy->health = enemy->health - offDamage; + if (enemy->health > 0) + { + ally->health = ally->health - defDamage; + if (ally->health <= 0) + { + ally->~Unit(); + } + } + else + { + enemy->~Unit(); } - } else { - enemy->~Unit(); } -} -void Unit::onClick(SDL_Event event) { + void Unit::update_position(int posX, int posY) + { + this->x = posX; + this->y = posY; + } + + void Unit::onClick(SDL_EVENT event) + { - Unit &defender; - Unit &attacker; + Unit & defender; + Unit & attacker; - switch (event.button.button) { + switch (event.button.button) + { case SDL_BUTTON_LEFT: - - //we have to re-initialize the unit.state (probably to idle) + + // we have to re-initialize the unit.state (probably to idle) this->is_selected = true; - for (Unit &unit : units) { - if(inRange(unit)) { + for (Unit &unit : units) + { + if (inRange(unit)) + { unit.state = UNAVAILABLE; }; } - break; + break; case SDL_BUTTON_RIGHT: this->is_targeted = true; - for (Unit &unit : units) { - if(unit.state = UNAVAILABLE) { + for (Unit &unit : units) + { + if (unit.state = UNAVAILABLE) + { continue; } - if(unit.is_selected) { + if (unit.is_selected) + { attacker = &unit; } - if(unit.is_targeted) { + if (unit.is_targeted) + { defender = &unit; } } - if(attacker && defender) { + if (attacker && defender) + { attack(attacker, defender); break; - } else { + } + else + { stderr("Could not init the attack!"); break; } - + } } -} -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; + 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; } - return false; -} -void Unit::loadXML(const char* filename) { + void Unit::loadXML(const char *filename) + { - tinyxml2::XMLDocument doc; - if (doc.LoadFile(filename) != tinyxml2::XML_SUCCESS) { - std::cerr << "Fehler beim Laden der XML-Datei!" << std::endl; - return; - } + tinyxml2::XMLDocument doc; + if (doc.LoadFile(filename) != tinyxml2::XML_SUCCESS) + { + std::cerr << "Fehler beim Laden der XML-Datei!" << std::endl; + return; + } + + MatchupTabel damageMatrix; + tinyxml2::XMLElement *unitElement = doc.FirstChildElement("Units")->FirstChildElement("Unit"); - MatchupTabel damageMatrix; - tinyxml2::XMLElement* unitElement = doc.FirstChildElement("Units")->FirstChildElement("Unit"); + // get all Units + while (unitElement) + { - //get all Units - while(unitElement) { + const u_int8_t UnitId = unitElement->Attribute("id"); - const u_int8_t UnitId = unitElement->Attribute("id"); + std::unordered_map<u_int8_t, int> attackValues; + tinyxml2::XMLElement *attackElement = unitElement->FirstChildElement("Attack"); - std::unordered_map<u_int8_t, int> attackValues; - tinyxml2::XMLElement* attackElement = unitElement->FirstChildElement("Attack"); + // get all attack-values + while (attackElement) + { - //get all attack-values - while(attackElement) { - - tinyxml2::XMLElement* attackTypeElement = attackElement->FirstChildElement(); + tinyxml2::XMLElement *attackTypeElement = attackElement->FirstChildElement(); - while(attackTypeElement) { - UnitId Unit_Id = static_cast<UnitId> attackTypeElement->Name(); //wenn das geht kauf ich maggus sein kochbuch + while (attackTypeElement) + { + UnitId Unit_Id = static_cast<UnitId> attackTypeElement->Name(); // wenn das geht kauf ich maggus sein kochbuch - int attackValue = attackTypeElement->IntText(); + int attackValue = attackTypeElement->IntText(); - attackValues[Unit_Id] = attackValue; + attackValues[Unit_Id] = attackValue; - attackTypeElement = attackTypeElement->NextSiblingElement(); + attackTypeElement = attackTypeElement->NextSiblingElement(); + } } - } - damageMatrix[unitElement][attackValues]; - unitElement = unitElement->NextSiblingElement("Unit"); + damageMatrix[unitElement][attackValues]; + unitElement = unitElement->NextSiblingElement("Unit"); + } } - -} } // namespace advanced_wars \ No newline at end of file diff --git a/src/unit.hpp b/src/unit.hpp index 9ead0b0..8710940 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -81,11 +81,12 @@ public: Will Update the health for both units Attacker deals damage to the defender first */ - void attack(Unit &enemy); + void attack(Unit &ally ,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); @@ -102,6 +103,11 @@ public: */ void loadXML(const char* filename); + +/* +This function will be called by an external event-handler, eventually. +Currently, it should be called if a Unit is interacted with and the resulting SDL_EVENT is passed through, and then decided upon +*/ void onClick(SDL_EVENT event); private: -- GitLab From c3aeb3d7c8b931caf8af5161fb414bbdeeaf2bdf Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Tue, 28 Jan 2025 18:18:17 +0100 Subject: [PATCH 12/30] Fix all possible bugs Had to switch from references to pointers for now; should be refactored in the future --- src/unit.cpp | 52 ++++++++++++++++++++++++++-------------------------- src/unit.hpp | 10 ++++++---- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index 83875bc..3a678b8 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -1,5 +1,6 @@ #include "unit.hpp" #include <tinyxml2.h> +#include <iostream> namespace advanced_wars { @@ -14,11 +15,6 @@ namespace advanced_wars { Spritesheet *spritesheet = engine.get_spritesheet(); - while (engine.events not empty) - { - handle_event(events.pop_front()); - } - int step = engine.get_stage() % spritesheet->get_unit_textures() .at(static_cast<int>(faction)) .at(static_cast<int>(id)) @@ -74,7 +70,10 @@ namespace advanced_wars } } - void Unit::attack(Unit &ally, Unit &enemy) + MatchupTabel damageMatrix; + std::vector<Unit*> units; + + void Unit::attack(Unit *ally, Unit *enemy) { if (ally->has_attacked) @@ -83,8 +82,8 @@ namespace advanced_wars } // Start Attack: choose the appropriate weapon: - int offDamage = damageMatrix[ally->id][enemy->id] * ((ally->health) / (ally->max_health)); - int defDamage = damageMatrix[ally->id][enemy->id] * ((enemy->health) / (enemy->max_health)); + int offDamage = damageMatrix[static_cast<u_int8_t>(ally->id)][static_cast<u_int8_t>(enemy->id)] * ((ally->health) / (ally->max_health)); + int defDamage = damageMatrix[static_cast<u_int8_t>(ally->id)][static_cast<u_int8_t>(enemy->id)] * ((enemy->health) / (enemy->max_health)); enemy->health = enemy->health - offDamage; if (enemy->health > 0) @@ -107,11 +106,11 @@ namespace advanced_wars this->y = posY; } - void Unit::onClick(SDL_EVENT event) + void Unit::onClick(SDL_Event event) { - Unit & defender; - Unit & attacker; + Unit *defender = nullptr; + Unit *attacker = nullptr; switch (event.button.button) { @@ -120,11 +119,11 @@ namespace advanced_wars // we have to re-initialize the unit.state (probably to idle) this->is_selected = true; - for (Unit &unit : units) + for (Unit *unit : units) { if (inRange(unit)) { - unit.state = UNAVAILABLE; + unit->state = advanced_wars::UnitState::UNAVAILABLE; }; } break; @@ -132,21 +131,21 @@ namespace advanced_wars this->is_targeted = true; - for (Unit &unit : units) + for (Unit *unit : units) { - if (unit.state = UNAVAILABLE) + if (unit->state == advanced_wars::UnitState::UNAVAILABLE) { continue; } - if (unit.is_selected) + if (unit->is_selected) { - attacker = &unit; + attacker = unit; } - if (unit.is_targeted) + if (unit->is_targeted) { - defender = &unit; + defender = unit; } } @@ -157,25 +156,25 @@ namespace advanced_wars } else { - stderr("Could not init the attack!"); + std::cerr << "Fehler beim Laden der XML-Datei!" << std::endl; break; } } } - bool Unit::inRange(Unit &enemy) + bool Unit::inRange(Unit *enemy) { - if (this->x == enemy.x) + if (this->x == enemy->x) { - return abs(this->y - enemy.y) <= this->range; + return abs(this->y - enemy->y) <= this->range; } - else if (this->y == enemy.y) + else if (this->y == enemy->y) { - return abs(this->x - enemy.x) <= this->range; + return abs(this->x - enemy->x) <= this->range; } return false; } - + /* void Unit::loadXML(const char *filename) { @@ -220,4 +219,5 @@ namespace advanced_wars unitElement = unitElement->NextSiblingElement("Unit"); } } + */ } // namespace advanced_wars \ No newline at end of file diff --git a/src/unit.hpp b/src/unit.hpp index 8710940..dfb5fc0 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -1,5 +1,6 @@ #pragma once +#include <unordered_map> #include "engine.hpp" #include "weapon.hpp" #include <optional> @@ -72,7 +73,7 @@ public: If a unit is selected, it should call inRange on all other enemy units on the field */ - bool inRange(Unit &enemy); + bool inRange(Unit *enemy); /* The attacker will move towards the defender and thus initiate combat @@ -81,7 +82,7 @@ public: Will Update the health for both units Attacker deals damage to the defender first */ - void attack(Unit &ally ,Unit &enemy); + void attack(Unit *ally ,Unit *enemy); /* @@ -108,7 +109,7 @@ void loadXML(const char* filename); This function will be called by an external event-handler, eventually. Currently, it should be called if a Unit is interacted with and the resulting SDL_EVENT is passed through, and then decided upon */ -void onClick(SDL_EVENT event); +void onClick(SDL_Event event); private: int x; @@ -131,9 +132,10 @@ private: // Primary weapon ammo int ammo; - + /* std::optional<Weapon> primary; std::optional<Weapon> secondary; + */ }; } // namespace advanced_wars \ No newline at end of file -- GitLab From 4b69d0a826cc96fad66c9df8576ca4206e00b275 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Tue, 28 Jan 2025 20:55:29 +0100 Subject: [PATCH 13/30] Add actual fighting mechanics Modifie Level.hpp with eventhandling (bare-bones) Fix potential bugs in unit.cpp --- src/level.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- src/level.hpp | 5 +++++ src/main.cpp | 2 +- src/unit.cpp | 27 ++++++++++++++++----------- src/unit.hpp | 9 +++++---- 5 files changed, 68 insertions(+), 18 deletions(-) diff --git a/src/level.cpp b/src/level.cpp index 74c1598..7469a8f 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -22,11 +22,50 @@ Level::Level(std::string name, int width, int height, std::vector<Tile> tiles, } }; -void Level::render(Engine &engine, std::vector<SDL_Event> &events) { - const int RENDERING_SCALE = 3; +const int RENDERING_SCALE = 3; + +bool Level::clickCheck(int mouseX, int mouseY) { + + int tileX = mouseX/(16*RENDERING_SCALE); + int tileY = mouseY/(16*RENDERING_SCALE); + + for (auto& unit : units) { + + if(unit.x == tileX && unit.y == tileY) { + //std::cout << "X:" << unit.x << "Y:" << unit.y << std::endl; + selectedUnit = &unit; + return true; + } + } +} + +void Level::handleEvent(Engine &engine, SDL_Event &event) { + + //handle following events: + //clicks/mouseDown + //escape (esc) + + switch (event.type) + { + case SDL_MOUSEBUTTONDOWN: + if(clickCheck(event.button.x, event.button.y)) { + selectedUnit->onClick(event, units); + } + break; + + default: + break; + } +} + +void Level::render(Engine &engine, std::vector<SDL_Event> &events) { + // Iterate over all events while (!events.empty()) { + //events.erase(events.begin()); + + handleEvent(engine, events.at(0)); events.erase(events.begin()); } diff --git a/src/level.hpp b/src/level.hpp index 9aff83c..3bf0dfb 100644 --- a/src/level.hpp +++ b/src/level.hpp @@ -23,6 +23,8 @@ public: void render(Engine &engine, std::vector<SDL_Event> &events); + void handleEvent(Engine &engine, SDL_Event &event); + private: std::string name; int width; @@ -31,6 +33,9 @@ private: std::vector<Building> buildings; std::vector<Unit> units; std::vector<Effect> effects; + Unit* selectedUnit; + + bool clickCheck(int mouseX, int mouseY); }; } // namespace advanced_wars diff --git a/src/main.cpp b/src/main.cpp index 6bb056f..a6d4dd4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -82,7 +82,7 @@ int main() { engine.set_scene(level); - Spritesheet spritesheet("/media/data/rust/sprite-extractor/spritesheet.h5", + Spritesheet spritesheet("./spritesheet.h5", engine); engine.set_spritesheet(spritesheet); diff --git a/src/unit.cpp b/src/unit.cpp index 3a678b8..8661298 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -6,7 +6,7 @@ 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) { health = max_health; }; @@ -106,7 +106,7 @@ namespace advanced_wars this->y = posY; } - void Unit::onClick(SDL_Event event) + void Unit::onClick(SDL_Event event, std::vector<Unit> &unitVector) { Unit *defender = nullptr; @@ -118,45 +118,50 @@ namespace advanced_wars // we have to re-initialize the unit.state (probably to idle) this->is_selected = true; + std::cout << "I am selected!!" << std::endl; + /* for (Unit *unit : units) { - if (inRange(unit)) + if (!inRange(unit)) { unit->state = advanced_wars::UnitState::UNAVAILABLE; }; } + */ break; case SDL_BUTTON_RIGHT: this->is_targeted = true; + std::cout << "I am targeted!!" << std::endl; - for (Unit *unit : units) + for (Unit unit : unitVector) { - if (unit->state == advanced_wars::UnitState::UNAVAILABLE) + if (unit.state == advanced_wars::UnitState::UNAVAILABLE) { continue; } - if (unit->is_selected) + if (unit.is_selected) { - attacker = unit; + attacker = &unit; } - if (unit->is_targeted) + if (unit.is_targeted) { - defender = unit; + defender = &unit; } } - if (attacker && defender) + if (attacker != nullptr && defender != nullptr) { attack(attacker, defender); + std::cout << "We are fighting!!" << std::endl; break; } else { - std::cerr << "Fehler beim Laden der XML-Datei!" << std::endl; + std::cerr << "Angriff konnte nicht gestartet werden!" << std::endl; break; } } diff --git a/src/unit.hpp b/src/unit.hpp index dfb5fc0..ef1c9ef 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -60,6 +60,10 @@ using MatchupTabel = std::unordered_map<u_int8_t, std::unordered_map<u_int8_t, i class Unit { public: + int x; + int y; + + Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state); ~Unit() { //Assuming that the destruktion of a unit triggers events @@ -109,12 +113,9 @@ void loadXML(const char* filename); This function will be called by an external event-handler, eventually. Currently, it should be called if a Unit is interacted with and the resulting SDL_EVENT is passed through, and then decided upon */ -void onClick(SDL_Event event); +void onClick(SDL_Event event, std::vector<Unit> &unitVector); private: - int x; - int y; - UnitFaction faction; UnitId id; UnitState state; -- GitLab From 70b3c32312f61c551c4526291675f16e4fb6ceea Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Tue, 28 Jan 2025 21:30:33 +0100 Subject: [PATCH 14/30] Add building selection --- src/level.cpp | 35 ++++++++++++++++++++++++++++++++++- src/level.hpp | 3 +++ src/unit.cpp | 15 +++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/level.cpp b/src/level.cpp index 7469a8f..6808505 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -29,6 +29,16 @@ bool Level::clickCheck(int mouseX, int mouseY) { int tileX = mouseX/(16*RENDERING_SCALE); int tileY = mouseY/(16*RENDERING_SCALE); + if(selectUnit(tileX, tileY)) return true; + if(selectBuilding(tileX, tileY)) return true; + + std::cout << "Neither building nor unit clicked" << std::endl; + + return false; +} + +bool Level::selectUnit (int tileX, int tileY) { + for (auto& unit : units) { if(unit.x == tileX && unit.y == tileY) { @@ -37,8 +47,23 @@ bool Level::clickCheck(int mouseX, int mouseY) { return true; } } + selectedUnit = nullptr; + return false; } +bool Level::selectBuilding (int tileX, int tileY) { + + for (auto& building : buildings) { + + if(building.x == tileX && building.y == tileY) { + //std::cout << "X:" << unit.x << "Y:" << unit.y << std::endl; + selectedBuilding = &building; + return true; + } + } + selectedBuilding = nullptr; + return false; +} void Level::handleEvent(Engine &engine, SDL_Event &event) { @@ -50,7 +75,15 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { { case SDL_MOUSEBUTTONDOWN: if(clickCheck(event.button.x, event.button.y)) { - selectedUnit->onClick(event, units); + + if(selectedUnit) { + selectedUnit->onClick(event, units); + } + + if(selectedBuilding) { + //building stuff + } + } break; diff --git a/src/level.hpp b/src/level.hpp index 3bf0dfb..0d3b6dd 100644 --- a/src/level.hpp +++ b/src/level.hpp @@ -34,6 +34,9 @@ private: std::vector<Unit> units; std::vector<Effect> effects; Unit* selectedUnit; + Building* selectedBuilding; + bool selectUnit (int tileX, int tileY); + bool selectBuilding(int tileX, int tileY); bool clickCheck(int mouseX, int mouseY); }; diff --git a/src/unit.cpp b/src/unit.cpp index 8661298..4b3f2e3 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -106,6 +106,19 @@ namespace advanced_wars this->y = posY; } +/* +Features: +//select unit + - show context menu + - show move range + - MAYBE show valid targets + +//deselect unit + +//attack unit + - show context menu + +*/ void Unit::onClick(SDL_Event event, std::vector<Unit> &unitVector) { @@ -129,6 +142,8 @@ namespace advanced_wars }; } */ + + //make move range calc break; case SDL_BUTTON_RIGHT: -- GitLab From abb13726f94d132e6f315470818ffbeceacc9819 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Tue, 28 Jan 2025 21:40:28 +0100 Subject: [PATCH 15/30] Add actual fighting --- src/unit.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/unit.cpp b/src/unit.cpp index 4b3f2e3..42fbab1 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -82,13 +82,18 @@ namespace advanced_wars } // Start Attack: choose the appropriate weapon: + /* int offDamage = damageMatrix[static_cast<u_int8_t>(ally->id)][static_cast<u_int8_t>(enemy->id)] * ((ally->health) / (ally->max_health)); - int defDamage = damageMatrix[static_cast<u_int8_t>(ally->id)][static_cast<u_int8_t>(enemy->id)] * ((enemy->health) / (enemy->max_health)); + int defDamage = damageMatrix[static_cast<u_int8_t>(enemy->id)][static_cast<u_int8_t>(ally->id)] * ((enemy->health) / (enemy->max_health)); + */ + int offDamage = 10; + int defDamage = 1000; enemy->health = enemy->health - offDamage; if (enemy->health > 0) { ally->health = ally->health - defDamage; + std::cout << "Health ally:" << ally->health << std::endl; if (ally->health <= 0) { ally->~Unit(); -- GitLab From 1c234b83fbf81f16f116a4b736acab50e9176de5 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Tue, 28 Jan 2025 22:25:31 +0100 Subject: [PATCH 16/30] Implement removal of units on stage if defeated --- src/level.cpp | 26 ++++++++++++++++++++++++-- src/unit.cpp | 37 +++++++++---------------------------- src/unit.hpp | 7 +++++-- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/level.cpp b/src/level.cpp index 6808505..7d0f61e 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -8,6 +8,7 @@ #include <SDL.h> #include <iostream> #include <string> +#include <algorithm> namespace advanced_wars { @@ -74,7 +75,8 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { switch (event.type) { case SDL_MOUSEBUTTONDOWN: - if(clickCheck(event.button.x, event.button.y)) { + if (event.button.button == SDL_BUTTON_LEFT) { + if(clickCheck(event.button.x, event.button.y)) { if(selectedUnit) { selectedUnit->onClick(event, units); @@ -83,8 +85,28 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { if(selectedBuilding) { //building stuff } - } + } else if (event.button.button == SDL_BUTTON_RIGHT) { + + if(selectedUnit) { + int tileX = event.button.x/(16*RENDERING_SCALE); + int tileY = event.button.y/(16*RENDERING_SCALE); + for (auto& unit : units) { + + if(unit.x == tileX && unit.y == tileY) { + //std::cout << "X:" << unit.x << "Y:" << unit.y << std::endl; + + selectedUnit->attack(unit); + + units.erase( + std::remove_if(units.begin(), units.end(), + [](const Unit& unit) { return unit.health < 0; }), + units.end()); + } + } + } + } + break; default: diff --git a/src/unit.cpp b/src/unit.cpp index 42fbab1..4911a0d 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -72,36 +72,17 @@ namespace advanced_wars MatchupTabel damageMatrix; std::vector<Unit*> units; + + void Unit::attack(Unit& enemy) { - void Unit::attack(Unit *ally, Unit *enemy) - { - - if (ally->has_attacked) - { - // display the unit as not able to attack (maybe greyscale?) - } - - // Start Attack: choose the appropriate weapon: - /* - int offDamage = damageMatrix[static_cast<u_int8_t>(ally->id)][static_cast<u_int8_t>(enemy->id)] * ((ally->health) / (ally->max_health)); - int defDamage = damageMatrix[static_cast<u_int8_t>(enemy->id)][static_cast<u_int8_t>(ally->id)] * ((enemy->health) / (enemy->max_health)); - */ - int offDamage = 10; + int offDamage = 50; int defDamage = 1000; - enemy->health = enemy->health - offDamage; - if (enemy->health > 0) - { - ally->health = ally->health - defDamage; - std::cout << "Health ally:" << ally->health << std::endl; - if (ally->health <= 0) - { - ally->~Unit(); - } - } - else - { - enemy->~Unit(); + enemy.health = enemy.health - offDamage; + std::cout << "Enemy health:" << enemy.health << std::endl; + if (enemy.health > 0) { + this->health = this->health - defDamage; + std::cout << "Health ally:" << this->health << std::endl; } } @@ -175,7 +156,7 @@ Features: if (attacker != nullptr && defender != nullptr) { - attack(attacker, defender); + //attack(attacker, defender); std::cout << "We are fighting!!" << std::endl; break; } diff --git a/src/unit.hpp b/src/unit.hpp index ef1c9ef..9c7460f 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -62,6 +62,7 @@ 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); @@ -86,7 +87,8 @@ public: Will Update the health for both units Attacker deals damage to the defender first */ - void attack(Unit *ally ,Unit *enemy); + + void attack(Unit& enemy); /* @@ -109,6 +111,7 @@ public: void loadXML(const char* filename); + /* This function will be called by an external event-handler, eventually. Currently, it should be called if a Unit is interacted with and the resulting SDL_EVENT is passed through, and then decided upon @@ -120,7 +123,7 @@ private: UnitId id; UnitState state; - int health; //health equals max_health at construction + int max_health; // max_health required for damage_scaling int range; int fuel; -- GitLab From 5feb27d1fa3fe427cf5ec9e3c38ac25db0a9805f Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Wed, 29 Jan 2025 18:49:09 +0100 Subject: [PATCH 17/30] Refactor simple issues Such as typos, unused includes and unnecessary comments --- src/unit.cpp | 60 +--------------------------------------------------- src/unit.hpp | 14 ++++++------ 2 files changed, 7 insertions(+), 67 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index 4911a0d..d48d323 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -1,5 +1,4 @@ #include "unit.hpp" -#include <tinyxml2.h> #include <iostream> namespace advanced_wars @@ -69,9 +68,6 @@ namespace advanced_wars &src, &dst, 0, NULL, SDL_FLIP_NONE); } } - - MatchupTabel damageMatrix; - std::vector<Unit*> units; void Unit::attack(Unit& enemy) { @@ -119,17 +115,8 @@ Features: this->is_selected = true; std::cout << "I am selected!!" << std::endl; - /* - for (Unit *unit : units) - { - if (!inRange(unit)) - { - unit->state = advanced_wars::UnitState::UNAVAILABLE; - }; - } - */ - //make move range calc + break; case SDL_BUTTON_RIGHT: @@ -180,50 +167,5 @@ Features: } return false; } - /* - void Unit::loadXML(const char *filename) - { - - tinyxml2::XMLDocument doc; - if (doc.LoadFile(filename) != tinyxml2::XML_SUCCESS) - { - std::cerr << "Fehler beim Laden der XML-Datei!" << std::endl; - return; - } - - MatchupTabel damageMatrix; - tinyxml2::XMLElement *unitElement = doc.FirstChildElement("Units")->FirstChildElement("Unit"); - - // get all Units - while (unitElement) - { - - const u_int8_t UnitId = unitElement->Attribute("id"); - std::unordered_map<u_int8_t, int> attackValues; - tinyxml2::XMLElement *attackElement = unitElement->FirstChildElement("Attack"); - - // get all attack-values - while (attackElement) - { - - tinyxml2::XMLElement *attackTypeElement = attackElement->FirstChildElement(); - - while (attackTypeElement) - { - UnitId Unit_Id = static_cast<UnitId> attackTypeElement->Name(); // wenn das geht kauf ich maggus sein kochbuch - - int attackValue = attackTypeElement->IntText(); - - attackValues[Unit_Id] = attackValue; - - attackTypeElement = attackTypeElement->NextSiblingElement(); - } - } - - damageMatrix[unitElement][attackValues]; - unitElement = unitElement->NextSiblingElement("Unit"); - } - } - */ } // namespace advanced_wars \ No newline at end of file diff --git a/src/unit.hpp b/src/unit.hpp index 9c7460f..f547e59 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -55,8 +55,7 @@ enum class MovementType { LANDER = 5, }; -//Fill the MatchupTabel -using MatchupTabel = std::unordered_map<u_int8_t, std::unordered_map<u_int8_t, int>>; +using MatchupTable = std::unordered_map<u_int8_t, std::unordered_map<u_int8_t, int>>; class Unit { public: @@ -104,13 +103,12 @@ public: */ void calculate_movement(); - /* - Load the XML and iterate over the entire datastructure - For every Unit, get Unit_Id and create a map for Unit_Id -> {Unit_Id -> damageType} - */ -void loadXML(const char* filename); - + /* + This function fills the MatchupTable + It would be better if this table would be placed in the level + */ + void fill_matchupTable(); /* This function will be called by an external event-handler, eventually. -- GitLab From 91cd1cb4cd97aa9b62f1f1dbb234f83359e13d23 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Wed, 29 Jan 2025 19:13:14 +0100 Subject: [PATCH 18/30] Make progress towards porting new attack function Currently the matchupTable needs to be loaded for both primary and secondary weapontypes How do we do that? --- src/unit.cpp | 90 +++++++++++++++++++++++++++++++++++++++++++++++----- src/unit.hpp | 12 +++---- 2 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index d48d323..9ef601d 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -69,16 +69,90 @@ namespace advanced_wars } } - void Unit::attack(Unit& enemy) { + void Unit::attack(Unit &enemy) + { + secondary_weapon = fill_matchupTable(0); + primary_weapon = fill_matchupTable(1); - int offDamage = 50; - int defDamage = 1000; + // Zuerst die Tabel für die Waffen der angreifenden Einheit holen + auto &attackerSecondaryWeaponTable = secondary_weapon[this->id]; + auto &attackerPrimaryWeaponTable = primary_weapon[this->id]; - enemy.health = enemy.health - offDamage; - std::cout << "Enemy health:" << enemy.health << std::endl; - if (enemy.health > 0) { - this->health = this->health - defDamage; - std::cout << "Health ally:" << this->health << std::endl; + // Schadenswert für die angreifende Einheit gegen die verteidigende Einheit berechnen + // Es wird die Waffe genommen die mehr Schaden macht + + int attackerDamageValue = 0; + + if (attackerSecondaryWeaponTable.find(enemy.id) != attackerSecondaryWeaponTable.end()) + { + attackerDamageValue = attackerSecondaryWeaponTable[enemy.id]; + } + if (attackerPrimaryWeaponTable.find(enemy.id) != attackerPrimaryWeaponTable.end()) + { + if (attackerDamageValue < attackerPrimaryWeaponTable[enemy.id]) + { + // Here ammo deduction should happen if applicable + attackerDamageValue = attackerPrimaryWeaponTable[enemy.id]; + } + } + + if (attackerDamageValue == 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 offDamage = attackerDamageValue * (static_cast<float>(health) / max_health); + enemy.health -= offDamage; + enemy.health = std::max(0, enemy.health); // Ensuring health is not negative + 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 &defenderSecondaryWeaponTable = secondary_weapon[enemy.id]; + auto &defenderPrimaryWeaponTable = primary_weapon[enemy.id]; + + int defenderDamageValue = 0; // Declare outside for later use + + // Determine the damage value for the defender + if (defenderSecondaryWeaponTable.find(id) != defenderSecondaryWeaponTable.end()) + { + defenderDamageValue = defenderSecondaryWeaponTable[id]; + } + if (defenderPrimaryWeaponTable.find(id) != defenderPrimaryWeaponTable.end()) + { + if (defenderDamageValue < defenderPrimaryWeaponTable[id]) + { + // Deduct ammo for primary weapon, if applicable + defenderDamageValue = defenderPrimaryWeaponTable[id]; + } + } + + // If a valid damage value was determined for retaliation + if (defenderDamageValue > 0) + { + int defDamage = static_cast<int>(defenderDamageValue * static_cast<float>(enemy.health) / enemy.max_health); + this->health -= defDamage; + this->health = std::max(0, this->health); // Safeguard against negative health + std::cout << "Ally health after retaliation: " << this->health << std::endl; + } + } + } + } + + MatchupTable Unit::fill_matchupTable(int type) { + switch (type) + { + case 0: + + break; + + default: + break; } } diff --git a/src/unit.hpp b/src/unit.hpp index f547e59..4027d81 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -55,7 +55,7 @@ enum class MovementType { LANDER = 5, }; -using MatchupTable = std::unordered_map<u_int8_t, std::unordered_map<u_int8_t, int>>; +using MatchupTable = std::unordered_map<UnitId, std::unordered_map<UnitId, int>>; class Unit { public: @@ -108,7 +108,7 @@ public: This function fills the MatchupTable It would be better if this table would be placed in the level */ - void fill_matchupTable(); + MatchupTable fill_matchupTable(int); /* This function will be called by an external event-handler, eventually. @@ -131,13 +131,11 @@ private: bool has_attacked; bool is_selected; bool is_targeted; + MatchupTable secondary_weapon; + MatchupTable primary_weapon; - // Primary weapon ammo int ammo; - /* - std::optional<Weapon> primary; - std::optional<Weapon> secondary; - */ + }; } // namespace advanced_wars \ No newline at end of file -- GitLab From af6c8e0607520342be2f1822eb49c10fc144f384 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Thu, 30 Jan 2025 10:13:15 +0100 Subject: [PATCH 19/30] Fix bug in selecting units New problems: - illegal instruction error - units disappear after moving them --- src/level.cpp | 102 ++++++++++++++++++++++++++++++++++++-------------- src/level.hpp | 5 ++- src/unit.cpp | 30 ++++++++------- src/unit.hpp | 2 +- 4 files changed, 95 insertions(+), 44 deletions(-) diff --git a/src/level.cpp b/src/level.cpp index 7d0f61e..11d3b15 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -25,30 +25,63 @@ Level::Level(std::string name, int width, int height, std::vector<Tile> tiles, const int RENDERING_SCALE = 3; -bool Level::clickCheck(int mouseX, int mouseY) { +bool Level::click_check_left(int mouseX, int mouseY) { int tileX = mouseX/(16*RENDERING_SCALE); int tileY = mouseY/(16*RENDERING_SCALE); - if(selectUnit(tileX, tileY)) return true; - if(selectBuilding(tileX, tileY)) return true; + if(selectUnit(tileX, tileY)) { + return true; + } + + if(selectBuilding(tileX, tileY)) { + return true; + } + + return false; +} - std::cout << "Neither building nor unit clicked" << std::endl; +bool Level::click_check_right(int mouseX, int mouseY) { + int tileX = mouseX/(16*RENDERING_SCALE); + int tileY = mouseY/(16*RENDERING_SCALE); + + 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& unit : units) { if(unit.x == tileX && unit.y == tileY) { - //std::cout << "X:" << unit.x << "Y:" << unit.y << std::endl; + //std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; + selectedUnit = &unit; return true; } } - selectedUnit = nullptr; + + return false; +} + +bool Level::target_unit (int tileX, int tileY) { + + //std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; + for (auto& unit : units) { + + if(unit.x == tileX && unit.y == tileY) { + //std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; + + targetedUnit = &unit; + return true; + } + } + return false; } @@ -62,7 +95,6 @@ bool Level::selectBuilding (int tileX, int tileY) { return true; } } - selectedBuilding = nullptr; return false; } @@ -75,44 +107,58 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { switch (event.type) { case SDL_MOUSEBUTTONDOWN: + if (event.button.button == SDL_BUTTON_LEFT) { - if(clickCheck(event.button.x, event.button.y)) { + + if(click_check_left(event.button.x, event.button.y)) { - if(selectedUnit) { - selectedUnit->onClick(event, units); - } + if(selectedUnit) { + selectedUnit->onClick(event, units); + } + + if(selectedBuilding) { + //building stuff + } + + } else { + + std::cout << "Neither building nor unit clicked!" << std::endl; + selectedUnit = nullptr; + selectedBuilding = nullptr; - if(selectedBuilding) { - //building stuff - } } + } else if (event.button.button == SDL_BUTTON_RIGHT) { if(selectedUnit) { - int tileX = event.button.x/(16*RENDERING_SCALE); - int tileY = event.button.y/(16*RENDERING_SCALE); - for (auto& unit : units) { - if(unit.x == tileX && unit.y == tileY) { - //std::cout << "X:" << unit.x << "Y:" << unit.y << std::endl; - - selectedUnit->attack(unit); + int tileX = event.button.x; + int tileY = event.button.y; + + if(click_check_right(tileX, tileY)) { + + selectedUnit->attack(targetedUnit); units.erase( std::remove_if(units.begin(), units.end(), [](const Unit& unit) { return unit.health < 0; }), units.end()); + + } else { + + + + selectedUnit->update_position(tileX, tileY); + } - } + } else { + + std::cout << "No unit selected! " << std::endl; + + } } } - - break; - - default: - break; } -} void Level::render(Engine &engine, std::vector<SDL_Event> &events) { diff --git a/src/level.hpp b/src/level.hpp index 0d3b6dd..7c5e99b 100644 --- a/src/level.hpp +++ b/src/level.hpp @@ -34,11 +34,14 @@ private: std::vector<Unit> units; std::vector<Effect> effects; Unit* selectedUnit; + Unit* targetedUnit; Building* selectedBuilding; bool selectUnit (int tileX, int tileY); + bool target_unit (int tileX, int tileY); bool selectBuilding(int tileX, int tileY); - bool clickCheck(int mouseX, int mouseY); + bool click_check_left(int mouseX, int mouseY); + bool click_check_right(int mouseX, int mouseY); }; } // namespace advanced_wars diff --git a/src/unit.cpp b/src/unit.cpp index 9ef601d..1d9b293 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -69,7 +69,7 @@ namespace advanced_wars } } - void Unit::attack(Unit &enemy) + void Unit::attack(Unit *enemy) { secondary_weapon = fill_matchupTable(0); primary_weapon = fill_matchupTable(1); @@ -83,38 +83,38 @@ namespace advanced_wars int attackerDamageValue = 0; - if (attackerSecondaryWeaponTable.find(enemy.id) != attackerSecondaryWeaponTable.end()) + if (attackerSecondaryWeaponTable.find(enemy->id) != attackerSecondaryWeaponTable.end()) { - attackerDamageValue = attackerSecondaryWeaponTable[enemy.id]; + attackerDamageValue = attackerSecondaryWeaponTable[enemy->id]; } - if (attackerPrimaryWeaponTable.find(enemy.id) != attackerPrimaryWeaponTable.end()) + if (attackerPrimaryWeaponTable.find(enemy->id) != attackerPrimaryWeaponTable.end()) { - if (attackerDamageValue < attackerPrimaryWeaponTable[enemy.id]) + if (attackerDamageValue < attackerPrimaryWeaponTable[enemy->id]) { // Here ammo deduction should happen if applicable - attackerDamageValue = attackerPrimaryWeaponTable[enemy.id]; + attackerDamageValue = attackerPrimaryWeaponTable[enemy->id]; } } if (attackerDamageValue == 0) { std::cout << "No damage value found for attack from unit " << static_cast<int>(id) - << " against unit " << static_cast<int>(enemy.id) << std::endl; + << " against unit " << static_cast<int>(enemy->id) << std::endl; } else { int offDamage = attackerDamageValue * (static_cast<float>(health) / max_health); - enemy.health -= offDamage; - enemy.health = std::max(0, enemy.health); // Ensuring health is not negative - std::cout << "Enemy health after attack: " << enemy.health << std::endl; + enemy->health -= offDamage; + enemy->health = std::max(0, enemy->health); // Ensuring health is not negative + 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) + if (enemy->health > 0) { // Weapon tables for the defender - auto &defenderSecondaryWeaponTable = secondary_weapon[enemy.id]; - auto &defenderPrimaryWeaponTable = primary_weapon[enemy.id]; + auto &defenderSecondaryWeaponTable = secondary_weapon[enemy->id]; + auto &defenderPrimaryWeaponTable = primary_weapon[enemy->id]; int defenderDamageValue = 0; // Declare outside for later use @@ -135,7 +135,7 @@ namespace advanced_wars // If a valid damage value was determined for retaliation if (defenderDamageValue > 0) { - int defDamage = static_cast<int>(defenderDamageValue * static_cast<float>(enemy.health) / enemy.max_health); + int defDamage = static_cast<int>(defenderDamageValue * static_cast<float>(enemy->health) / enemy->max_health); this->health -= defDamage; this->health = std::max(0, this->health); // Safeguard against negative health std::cout << "Ally health after retaliation: " << this->health << std::endl; @@ -188,6 +188,7 @@ Features: // we have to re-initialize the unit.state (probably to idle) this->is_selected = true; std::cout << "I am selected!!" << std::endl; + std::cout << "And my position is:" << this->x << " " << this->y << std::endl; //make move range calc @@ -196,6 +197,7 @@ Features: this->is_targeted = true; std::cout << "I am targeted!!" << std::endl; + std::cout << "And my position is:" << this->x << " " << this->y << std::endl; for (Unit unit : unitVector) { diff --git a/src/unit.hpp b/src/unit.hpp index 4027d81..d836862 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -87,7 +87,7 @@ public: Attacker deals damage to the defender first */ - void attack(Unit& enemy); + void attack(Unit *enemy); /* -- GitLab From 3528a3d647cfce740ce7736aa9e4f1e72de5d75f Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Thu, 30 Jan 2025 10:23:17 +0100 Subject: [PATCH 20/30] Refactor left_click Why does the clicked unit dissappear after moving it? --- src/level.cpp | 6 ++---- src/unit.cpp | 55 +++------------------------------------------------ src/unit.hpp | 4 ++-- 3 files changed, 7 insertions(+), 58 deletions(-) diff --git a/src/level.cpp b/src/level.cpp index 11d3b15..0b241d1 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -113,7 +113,7 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { if(click_check_left(event.button.x, event.button.y)) { if(selectedUnit) { - selectedUnit->onClick(event, units); + selectedUnit->on_left_click(event, units); } if(selectedBuilding) { @@ -146,8 +146,6 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { } else { - - selectedUnit->update_position(tileX, tileY); } @@ -156,8 +154,8 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { std::cout << "No unit selected! " << std::endl; } - } } + } } void Level::render(Engine &engine, std::vector<SDL_Event> &events) { diff --git a/src/unit.cpp b/src/unit.cpp index 1d9b293..c0a4634 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -175,60 +175,11 @@ Features: - show context menu */ - void Unit::onClick(SDL_Event event, std::vector<Unit> &unitVector) + void Unit::on_left_click(SDL_Event event, std::vector<Unit> &unitVector) { - Unit *defender = nullptr; - Unit *attacker = nullptr; - - switch (event.button.button) - { - case SDL_BUTTON_LEFT: - - // we have to re-initialize the unit.state (probably to idle) - this->is_selected = true; - std::cout << "I am selected!!" << std::endl; - std::cout << "And my position is:" << this->x << " " << this->y << std::endl; - - //make move range calc - - break; - case SDL_BUTTON_RIGHT: - - this->is_targeted = true; - std::cout << "I am targeted!!" << std::endl; - std::cout << "And my position is:" << this->x << " " << this->y << std::endl; - - for (Unit unit : unitVector) - { - if (unit.state == advanced_wars::UnitState::UNAVAILABLE) - { - continue; - } - - if (unit.is_selected) - { - attacker = &unit; - } - - if (unit.is_targeted) - { - defender = &unit; - } - } - - if (attacker != nullptr && defender != nullptr) - { - //attack(attacker, defender); - std::cout << "We are fighting!!" << std::endl; - break; - } - else - { - std::cerr << "Angriff konnte nicht gestartet werden!" << std::endl; - break; - } - } + std::cout << "Left-button pressed on unit: " << this->health << std::endl; + } bool Unit::inRange(Unit *enemy) diff --git a/src/unit.hpp b/src/unit.hpp index d836862..9550219 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -112,9 +112,9 @@ public: /* This function will be called by an external event-handler, eventually. -Currently, it should be called if a Unit is interacted with and the resulting SDL_EVENT is passed through, and then decided upon +It should start displaying standard unit information, such as UI and move_range */ -void onClick(SDL_Event event, std::vector<Unit> &unitVector); +void on_left_click(SDL_Event event, std::vector<Unit> &unitVector); private: UnitFaction faction; -- GitLab From 19e1fdeca94821f7050c328546d8b6243602401f Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Thu, 30 Jan 2025 12:26:08 +0100 Subject: [PATCH 21/30] Save work for soon --- src/level.cpp | 12 ++++-------- src/unit.cpp | 13 ------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/src/level.cpp b/src/level.cpp index 0b241d1..a1e0b98 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -100,10 +100,6 @@ bool Level::selectBuilding (int tileX, int tileY) { void Level::handleEvent(Engine &engine, SDL_Event &event) { - //handle following events: - //clicks/mouseDown - //escape (esc) - switch (event.type) { case SDL_MOUSEBUTTONDOWN: @@ -120,11 +116,11 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { //building stuff } - } else { + } else { - std::cout << "Neither building nor unit clicked!" << std::endl; - selectedUnit = nullptr; - selectedBuilding = nullptr; + std::cout << "Neither building nor unit clicked!" << std::endl; + selectedUnit = nullptr; + selectedBuilding = nullptr; } diff --git a/src/unit.cpp b/src/unit.cpp index c0a4634..311e026 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -162,19 +162,6 @@ namespace advanced_wars this->y = posY; } -/* -Features: -//select unit - - show context menu - - show move range - - MAYBE show valid targets - -//deselect unit - -//attack unit - - show context menu - -*/ void Unit::on_left_click(SDL_Event event, std::vector<Unit> &unitVector) { -- GitLab From 5b0d77b729f276b27c0daa966d40d3b89e80c897 Mon Sep 17 00:00:00 2001 From: Lorenz <lorenz-martin.diel@informatik.hs-fulda.de> Date: Thu, 30 Jan 2025 13:05:02 +0100 Subject: [PATCH 22/30] Fixed Bug: units no longer dissappear when they are being placed --- src/level.cpp | 289 ++++++++++++++++++++++++++++---------------------- src/unit.cpp | 12 ++- 2 files changed, 170 insertions(+), 131 deletions(-) diff --git a/src/level.cpp b/src/level.cpp index 11d3b15..2ac150a 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -8,193 +8,224 @@ #include <SDL.h> #include <iostream> #include <string> -#include <algorithm> +#include <algorithm> -namespace advanced_wars { +namespace advanced_wars +{ -Level::Level(std::string name, int width, int height, std::vector<Tile> tiles, - std::vector<Building> buildings, std::vector<Unit> units, - std::vector<Effect> effects) - : name(name), width(width), height(height), tiles(tiles), - buildings(buildings), units(units), effects(effects) { + Level::Level(std::string name, int width, int height, std::vector<Tile> tiles, + std::vector<Building> buildings, std::vector<Unit> units, + std::vector<Effect> effects) + : name(name), width(width), height(height), tiles(tiles), + buildings(buildings), units(units), effects(effects) + { - if ((size_t)(width * height) != tiles.size()) { - throw std::runtime_error("level tile mismatch"); - } -}; + if ((size_t)(width * height) != tiles.size()) + { + throw std::runtime_error("level tile mismatch"); + } + }; -const int RENDERING_SCALE = 3; + const int RENDERING_SCALE = 3; -bool Level::click_check_left(int mouseX, int mouseY) { - - int tileX = mouseX/(16*RENDERING_SCALE); - int tileY = mouseY/(16*RENDERING_SCALE); + bool Level::click_check_left(int mouseX, int mouseY) + { - if(selectUnit(tileX, tileY)) { - return true; - } + int tileX = mouseX / (16 * RENDERING_SCALE); + int tileY = mouseY / (16 * RENDERING_SCALE); - if(selectBuilding(tileX, tileY)) { - return true; - } - - return false; -} + if (selectUnit(tileX, tileY)) + { + return true; + } -bool Level::click_check_right(int mouseX, int mouseY) { - - int tileX = mouseX/(16*RENDERING_SCALE); - int tileY = mouseY/(16*RENDERING_SCALE); + if (selectBuilding(tileX, tileY)) + { + return true; + } - if(target_unit(tileX, tileY)) { - return true; + return false; } - - return false; -} -bool Level::selectUnit (int tileX, int tileY) { + bool Level::click_check_right(int mouseX, int mouseY) + { - //std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; - for (auto& unit : units) { + int tileX = mouseX / (16 * RENDERING_SCALE); + int tileY = mouseY / (16 * RENDERING_SCALE); - if(unit.x == tileX && unit.y == tileY) { - //std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; - - selectedUnit = &unit; + if (target_unit(tileX, tileY)) + { return true; } + + return false; } - - return false; -} -bool Level::target_unit (int tileX, int tileY) { + bool Level::selectUnit(int tileX, int tileY) + { - //std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; - for (auto& unit : units) { + // std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; + for (auto &unit : units) + { - if(unit.x == tileX && unit.y == tileY) { - //std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; - - targetedUnit = &unit; - return true; + if (unit.x == tileX && unit.y == tileY) + { + // std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; + + selectedUnit = &unit; + return true; + } } + + return false; } - - return false; -} -bool Level::selectBuilding (int tileX, int tileY) { + bool Level::target_unit(int tileX, int tileY) + { + + // std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; + for (auto &unit : units) + { - for (auto& building : buildings) { + if (unit.x == tileX && unit.y == tileY) + { + // std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; - if(building.x == tileX && building.y == tileY) { - //std::cout << "X:" << unit.x << "Y:" << unit.y << std::endl; - selectedBuilding = &building; - return true; + targetedUnit = &unit; + return true; + } } + + return false; } - return false; -} -void Level::handleEvent(Engine &engine, SDL_Event &event) { + bool Level::selectBuilding(int tileX, int tileY) + { - //handle following events: - //clicks/mouseDown - //escape (esc) + for (auto &building : buildings) + { - switch (event.type) + if (building.x == tileX && building.y == tileY) + { + // std::cout << "X:" << unit.x << "Y:" << unit.y << std::endl; + selectedBuilding = &building; + return true; + } + } + return false; + } + + void Level::handleEvent(Engine &engine, SDL_Event &event) { - case SDL_MOUSEBUTTONDOWN: - if (event.button.button == SDL_BUTTON_LEFT) { + // handle following events: + // clicks/mouseDown + // escape (esc) + + switch (event.type) + { + case SDL_MOUSEBUTTONDOWN: - if(click_check_left(event.button.x, event.button.y)) { - - if(selectedUnit) { + if (event.button.button == SDL_BUTTON_LEFT) + { + + if (click_check_left(event.button.x, event.button.y)) + { + + if (selectedUnit) + { selectedUnit->onClick(event, units); } - if(selectedBuilding) { - //building stuff + if (selectedBuilding) + { + // building stuff } + } + else + { - } else { - - std::cout << "Neither building nor unit clicked!" << std::endl; - selectedUnit = nullptr; - selectedBuilding = nullptr; - + std::cout << "Neither building nor unit clicked!" << std::endl; + selectedUnit = nullptr; + selectedBuilding = nullptr; + } } + else if (event.button.button == SDL_BUTTON_RIGHT) + { - } else if (event.button.button == SDL_BUTTON_RIGHT) { - - if(selectedUnit) { - - int tileX = event.button.x; - int tileY = event.button.y; - - if(click_check_right(tileX, tileY)) { + if (selectedUnit) + { + if (click_check_right(event.button.x, event.button.y)) + { + selectedUnit->attack(targetedUnit); units.erase( - std::remove_if(units.begin(), units.end(), - [](const Unit& unit) { return unit.health < 0; }), - units.end()); - - } else { - - + std::remove_if(units.begin(), units.end(), + [](const Unit &unit) + { return unit.health < 0; }), + units.end()); + } + else + { + int tileX = event.button.x / (16 * RENDERING_SCALE); + int tileY = event.button.y / (16 * RENDERING_SCALE); selectedUnit->update_position(tileX, tileY); - } - } else { + } + else + { std::cout << "No unit selected! " << std::endl; - - } } } + } } -void Level::render(Engine &engine, std::vector<SDL_Event> &events) { - - // Iterate over all events - while (!events.empty()) { - //events.erase(events.begin()); + void Level::render(Engine &engine, std::vector<SDL_Event> &events) + { - handleEvent(engine, events.at(0)); - events.erase(events.begin()); - } + // Iterate over all events + while (!events.empty()) + { + // events.erase(events.begin()); - // Tiles - for (Tile &tile : tiles) { - tile.render(engine, RENDERING_SCALE); - } + handleEvent(engine, events.at(0)); + events.erase(events.begin()); + } - // Buildings - for (Building &building : buildings) { - building.render(engine, RENDERING_SCALE); - } + // Tiles + for (Tile &tile : tiles) + { + tile.render(engine, RENDERING_SCALE); + } - // Units - for (Unit &unit : units) { - unit.render(engine, RENDERING_SCALE); - } + // Buildings + for (Building &building : buildings) + { + building.render(engine, RENDERING_SCALE); + } - // Effects - for (Effect &effect : effects) { - effect.render(engine, RENDERING_SCALE); - } + // Units + for (Unit &unit : units) + { + unit.render(engine, RENDERING_SCALE); + } - // Set background color for renderer - if (SDL_SetRenderDrawColor(engine.renderer(), 255, 0, 0, 0)) { - std::cout << "Could not set render draw color: " << SDL_GetError() - << std::endl; + // Effects + for (Effect &effect : effects) + { + effect.render(engine, RENDERING_SCALE); + } + + // Set background color for renderer + if (SDL_SetRenderDrawColor(engine.renderer(), 255, 0, 0, 0)) + { + std::cout << "Could not set render draw color: " << SDL_GetError() + << std::endl; + } } -} } // namespace advanced_wars diff --git a/src/unit.cpp b/src/unit.cpp index 1d9b293..21b0735 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -74,10 +74,14 @@ namespace advanced_wars secondary_weapon = fill_matchupTable(0); primary_weapon = fill_matchupTable(1); + + // Zuerst die Tabel für die Waffen der angreifenden Einheit holen auto &attackerSecondaryWeaponTable = secondary_weapon[this->id]; auto &attackerPrimaryWeaponTable = primary_weapon[this->id]; + + // Schadenswert für die angreifende Einheit gegen die verteidigende Einheit berechnen // Es wird die Waffe genommen die mehr Schaden macht @@ -87,6 +91,7 @@ namespace advanced_wars { attackerDamageValue = attackerSecondaryWeaponTable[enemy->id]; } + if (attackerPrimaryWeaponTable.find(enemy->id) != attackerPrimaryWeaponTable.end()) { if (attackerDamageValue < attackerPrimaryWeaponTable[enemy->id]) @@ -96,6 +101,8 @@ namespace advanced_wars } } + + if (attackerDamageValue == 0) { std::cout << "No damage value found for attack from unit " << static_cast<int>(id) @@ -159,8 +166,9 @@ namespace advanced_wars void Unit::update_position(int posX, int posY) { this->x = posX; - this->y = posY; - } + this->y = posY; + } + /* Features: -- GitLab From 97a2125b26289a1038a08b60e0fa40dcbd56dcb4 Mon Sep 17 00:00:00 2001 From: Lorenz <lorenz-martin.diel@informatik.hs-fulda.de> Date: Thu, 30 Jan 2025 15:28:24 +0100 Subject: [PATCH 23/30] adjusted attack function on DamageTable in weapon --- src/unit.cpp | 114 +++++++++++++++++++++++-------------------------- src/unit.hpp | 4 +- src/weapon.cpp | 26 ++++++++++- src/weapon.hpp | 22 +++++++--- 4 files changed, 96 insertions(+), 70 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index 21b0735..81f4312 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -7,6 +7,11 @@ 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), max_health(100) { + //das ist nur für Testzwecke + if (id == UnitId::INFANTERY) + { + secondary_weapon = Weapon("Machine-Gun", {{UnitId::INFANTERY, 55}}); + } health = max_health; }; @@ -68,82 +73,71 @@ namespace advanced_wars &src, &dst, 0, NULL, SDL_FLIP_NONE); } } - - void Unit::attack(Unit *enemy) - { - secondary_weapon = fill_matchupTable(0); - primary_weapon = fill_matchupTable(1); - - - // Zuerst die Tabel für die Waffen der angreifenden Einheit holen - auto &attackerSecondaryWeaponTable = secondary_weapon[this->id]; - auto &attackerPrimaryWeaponTable = primary_weapon[this->id]; - - - - // Schadenswert für die angreifende Einheit gegen die verteidigende Einheit berechnen - // Es wird die Waffe genommen die mehr Schaden macht + 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 attackerDamageValue = 0; + int attacker_damage_value = 0; - if (attackerSecondaryWeaponTable.find(enemy->id) != attackerSecondaryWeaponTable.end()) + // Die Waffe mit dem höchsten Schaden wählen + if (secondary_weapon_damage_it != secondary_weapon.damage.end()) { - attackerDamageValue = attackerSecondaryWeaponTable[enemy->id]; + attacker_damage_value = secondary_weapon_damage_it->second; } - - if (attackerPrimaryWeaponTable.find(enemy->id) != attackerPrimaryWeaponTable.end()) + + if (primary_weapon_damage_it != primary_weapon.damage.end()) { - if (attackerDamageValue < attackerPrimaryWeaponTable[enemy->id]) + if (primary_weapon_damage_it->second > attacker_damage_value) { - // Here ammo deduction should happen if applicable - attackerDamageValue = attackerPrimaryWeaponTable[enemy->id]; + // Munitionsabzug sollte hier erfolgen, falls zutreffend + attacker_damage_value = primary_weapon_damage_it->second; } } - - - if (attackerDamageValue == 0) + 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 offDamage = attackerDamageValue * (static_cast<float>(health) / max_health); - enemy->health -= offDamage; - enemy->health = std::max(0, enemy->health); // Ensuring health is not negative + 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 &defenderSecondaryWeaponTable = secondary_weapon[enemy->id]; - auto &defenderPrimaryWeaponTable = primary_weapon[enemy->id]; + 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 defenderDamageValue = 0; // Declare outside for later use + int defender_damage_value = 0; // Declare outside for later use // Determine the damage value for the defender - if (defenderSecondaryWeaponTable.find(id) != defenderSecondaryWeaponTable.end()) + if (defender_secondary_weapon_damage_it != enemy->secondary_weapon.damage.end()) { - defenderDamageValue = defenderSecondaryWeaponTable[id]; + defender_damage_value = defender_secondary_weapon_damage_it->second; } - if (defenderPrimaryWeaponTable.find(id) != defenderPrimaryWeaponTable.end()) + + if (defender_primary_weapon_damage_it != enemy->primary_weapon.damage.end()) { - if (defenderDamageValue < defenderPrimaryWeaponTable[id]) + if (defender_primary_weapon_damage_it->second > defender_damage_value) { - // Deduct ammo for primary weapon, if applicable - defenderDamageValue = defenderPrimaryWeaponTable[id]; + // 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 (defenderDamageValue > 0) + if (defender_damage_value > 0) { - int defDamage = static_cast<int>(defenderDamageValue * static_cast<float>(enemy->health) / enemy->max_health); - this->health -= defDamage; + 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; } @@ -151,13 +145,14 @@ namespace advanced_wars } } - MatchupTable Unit::fill_matchupTable(int type) { + MatchupTable Unit::fill_matchupTable(int type) + { switch (type) { case 0: - + break; - + default: break; } @@ -166,23 +161,22 @@ namespace advanced_wars void Unit::update_position(int posX, int posY) { this->x = posX; - this->y = posY; - } - + this->y = posY; + } -/* -Features: -//select unit - - show context menu - - show move range - - MAYBE show valid targets + /* + Features: + //select unit + - show context menu + - show move range + - MAYBE show valid targets -//deselect unit + //deselect unit -//attack unit - - show context menu + //attack unit + - show context menu -*/ + */ void Unit::onClick(SDL_Event event, std::vector<Unit> &unitVector) { @@ -198,7 +192,7 @@ Features: std::cout << "I am selected!!" << std::endl; std::cout << "And my position is:" << this->x << " " << this->y << std::endl; - //make move range calc + // make move range calc break; case SDL_BUTTON_RIGHT: @@ -227,7 +221,7 @@ Features: if (attacker != nullptr && defender != nullptr) { - //attack(attacker, defender); + // attack(attacker, defender); std::cout << "We are fighting!!" << std::endl; break; } diff --git a/src/unit.hpp b/src/unit.hpp index d836862..ca588ea 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -131,8 +131,8 @@ private: bool has_attacked; bool is_selected; bool is_targeted; - MatchupTable secondary_weapon; - MatchupTable primary_weapon; + Weapon secondary_weapon; + Weapon primary_weapon; int ammo; diff --git a/src/weapon.cpp b/src/weapon.cpp index 650e950..b93d09c 100644 --- a/src/weapon.cpp +++ b/src/weapon.cpp @@ -1,3 +1,27 @@ #include "weapon.hpp" -namespace advanced_wars {} \ No newline at end of file +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 + } + +} \ No newline at end of file diff --git a/src/weapon.hpp b/src/weapon.hpp index 753e674..2192e61 100644 --- a/src/weapon.hpp +++ b/src/weapon.hpp @@ -1,21 +1,29 @@ #pragma once -#include "unit.hpp" +#include <string> #include <unordered_map> namespace advanced_wars { -// Forward Declaration enum class UnitId; class Weapon { public: - // Ranges - int min_range; - int max_range; + // Konstruktoren + Weapon(); + Weapon(const std::string &weaponName, const std::unordered_map<UnitId, int> &damageValues); - // Damage + // 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 \ No newline at end of file +} // namespace advanced_wars -- GitLab From 645c9bc0298e161b94cbf646e2997d56ba0eb2c6 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Thu, 30 Jan 2025 16:27:25 +0100 Subject: [PATCH 24/30] Add responsive Unitstates for movement --- src/level.cpp | 4 +- src/unit.cpp | 125 +++++++++++++++++++------------------------------- src/unit.hpp | 3 +- 3 files changed, 51 insertions(+), 81 deletions(-) diff --git a/src/level.cpp b/src/level.cpp index a1e0b98..f1eec01 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -128,8 +128,8 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { if(selectedUnit) { - int tileX = event.button.x; - int tileY = event.button.y; + int tileX = event.button.x / (16*RENDERING_SCALE); + int tileY = event.button.y / (16*RENDERING_SCALE); if(click_check_right(tileX, tileY)) { diff --git a/src/unit.cpp b/src/unit.cpp index 311e026..a2e2d80 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -69,97 +69,66 @@ namespace advanced_wars } } - void Unit::attack(Unit *enemy) - { - secondary_weapon = fill_matchupTable(0); - primary_weapon = fill_matchupTable(1); + void Unit::attack(Unit* enemy) { - // Zuerst die Tabel für die Waffen der angreifenden Einheit holen - auto &attackerSecondaryWeaponTable = secondary_weapon[this->id]; - auto &attackerPrimaryWeaponTable = primary_weapon[this->id]; + int offDamage = 50; + int defDamage = 50; - // Schadenswert für die angreifende Einheit gegen die verteidigende Einheit berechnen - // Es wird die Waffe genommen die mehr Schaden macht + enemy->health = enemy->health - offDamage; + std::cout << "Enemy health:" << enemy->health << std::endl; + if (enemy->health > 0) { + this->health = this->health - defDamage; + std::cout << "Health ally:" << this->health << std::endl; + } + } - int attackerDamageValue = 0; - if (attackerSecondaryWeaponTable.find(enemy->id) != attackerSecondaryWeaponTable.end()) - { - attackerDamageValue = attackerSecondaryWeaponTable[enemy->id]; - } - if (attackerPrimaryWeaponTable.find(enemy->id) != attackerPrimaryWeaponTable.end()) - { - if (attackerDamageValue < attackerPrimaryWeaponTable[enemy->id]) - { - // Here ammo deduction should happen if applicable - attackerDamageValue = attackerPrimaryWeaponTable[enemy->id]; + 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 currentX = this->x; + int currentY = this->y; + + int deltaX = currentX - posX; + int deltaY = currentY - posY; + + if (deltaY == 0) { + if (deltaX > 0) { + this->state = advanced_wars::UnitState::MOVEMENTLEFT; + return; + } else { + this->state = advanced_wars::UnitState::MOVEMENTRIGHT; + return; } } - if (attackerDamageValue == 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 - { + double bresen = deltaX/deltaY; - int offDamage = attackerDamageValue * (static_cast<float>(health) / max_health); - enemy->health -= offDamage; - enemy->health = std::max(0, enemy->health); // Ensuring health is not negative - 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 &defenderSecondaryWeaponTable = secondary_weapon[enemy->id]; - auto &defenderPrimaryWeaponTable = primary_weapon[enemy->id]; - - int defenderDamageValue = 0; // Declare outside for later use - - // Determine the damage value for the defender - if (defenderSecondaryWeaponTable.find(id) != defenderSecondaryWeaponTable.end()) - { - defenderDamageValue = defenderSecondaryWeaponTable[id]; - } - if (defenderPrimaryWeaponTable.find(id) != defenderPrimaryWeaponTable.end()) - { - if (defenderDamageValue < defenderPrimaryWeaponTable[id]) - { - // Deduct ammo for primary weapon, if applicable - defenderDamageValue = defenderPrimaryWeaponTable[id]; - } - } - - // If a valid damage value was determined for retaliation - if (defenderDamageValue > 0) - { - int defDamage = static_cast<int>(defenderDamageValue * static_cast<float>(enemy->health) / enemy->max_health); - this->health -= defDamage; - this->health = std::max(0, this->health); // Safeguard against negative health - std::cout << "Ally health after retaliation: " << this->health << std::endl; - } + if(bresen == 0) { + if(deltaY < 0) { + this->state = advanced_wars::UnitState::MOVEMENTDOWN; + return; + } else { + this->state = advanced_wars::UnitState::MOVEMENTUP; + return; } } - } - MatchupTable Unit::fill_matchupTable(int type) { - switch (type) - { - case 0: - - break; - - default: - break; + if(0 < bresen && bresen < 1) { + this->state = advanced_wars::UnitState::MOVEMENTDOWN; + return; + } else if (-1 < bresen && bresen < 0) { + this->state = advanced_wars::UnitState::MOVEMENTUP; + return; } - } - void Unit::update_position(int posX, int posY) - { - this->x = posX; - this->y = posY; } void Unit::on_left_click(SDL_Event event, std::vector<Unit> &unitVector) diff --git a/src/unit.hpp b/src/unit.hpp index 9550219..ad189e8 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -87,7 +87,7 @@ public: Attacker deals damage to the defender first */ - void attack(Unit *enemy); + void attack(Unit* enemy) ; /* @@ -103,6 +103,7 @@ public: */ void calculate_movement(); + void calc_state(int posX, int posY); /* This function fills the MatchupTable -- GitLab From cce05dedc0871144d0c69df4e8e1cb7921a59955 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Thu, 30 Jan 2025 16:36:40 +0100 Subject: [PATCH 25/30] fix bug concerning moving and attacking --- src/level.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/level.cpp b/src/level.cpp index f1eec01..456bb40 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -25,11 +25,8 @@ Level::Level(std::string name, int width, int height, std::vector<Tile> tiles, const int RENDERING_SCALE = 3; -bool Level::click_check_left(int mouseX, int mouseY) { +bool Level::click_check_left(int tileX, int tileY) { - int tileX = mouseX/(16*RENDERING_SCALE); - int tileY = mouseY/(16*RENDERING_SCALE); - if(selectUnit(tileX, tileY)) { return true; } @@ -41,10 +38,7 @@ bool Level::click_check_left(int mouseX, int mouseY) { return false; } -bool Level::click_check_right(int mouseX, int mouseY) { - - int tileX = mouseX/(16*RENDERING_SCALE); - int tileY = mouseY/(16*RENDERING_SCALE); +bool Level::click_check_right(int tileX, int tileY) { if(target_unit(tileX, tileY)) { return true; @@ -106,7 +100,10 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { if (event.button.button == SDL_BUTTON_LEFT) { - if(click_check_left(event.button.x, event.button.y)) { + int tileX = event.button.x/(16*RENDERING_SCALE); + int tileY = event.button.y/(16*RENDERING_SCALE); + + if(click_check_left(tileX,tileY)) { if(selectedUnit) { selectedUnit->on_left_click(event, units); @@ -137,7 +134,7 @@ void Level::handleEvent(Engine &engine, SDL_Event &event) { units.erase( std::remove_if(units.begin(), units.end(), - [](const Unit& unit) { return unit.health < 0; }), + [](const Unit& unit) { return unit.health <= 0; }), units.end()); } else { -- GitLab From 7fb409a0023a016759ef047ee766910a64e2cda5 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Thu, 30 Jan 2025 19:02:04 +0100 Subject: [PATCH 26/30] Removed unnecessary function --- src/unit.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index c71a0c0..af4a42d 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -145,19 +145,6 @@ namespace advanced_wars } } - MatchupTable Unit::fill_matchupTable(int type) - { - switch (type) - { - case 0: - - break; - - default: - break; - } - } - void Unit::update_position(int posX, int posY) { calc_state(posX, posY); -- GitLab From e1c9cb119335a7eb806d1ac662fea8905bc1cb55 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Thu, 30 Jan 2025 19:21:30 +0100 Subject: [PATCH 27/30] Cut some function parts from calc_state --- src/unit.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index af4a42d..d73e6b2 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -163,10 +163,10 @@ namespace advanced_wars if (deltaY == 0) { if (deltaX > 0) { - this->state = advanced_wars::UnitState::MOVEMENTLEFT; + this->state = UnitState::MOVEMENTLEFT; return; } else { - this->state = advanced_wars::UnitState::MOVEMENTRIGHT; + this->state = UnitState::MOVEMENTRIGHT; return; } } @@ -175,22 +175,13 @@ namespace advanced_wars if(bresen == 0) { if(deltaY < 0) { - this->state = advanced_wars::UnitState::MOVEMENTDOWN; + this->state = UnitState::MOVEMENTDOWN; return; } else { - this->state = advanced_wars::UnitState::MOVEMENTUP; + this->state = UnitState::MOVEMENTUP; return; } } - - if(0 < bresen && bresen < 1) { - this->state = advanced_wars::UnitState::MOVEMENTDOWN; - return; - } else if (-1 < bresen && bresen < 0) { - this->state = advanced_wars::UnitState::MOVEMENTUP; - return; - } - } void Unit::on_left_click(SDL_Event event, std::vector<Unit> &unitVector) -- GitLab From 6692c6ddae5a32f1500f24108380e7e3523619d5 Mon Sep 17 00:00:00 2001 From: Max Cherris <MCherris@protonmail.com> Date: Thu, 30 Jan 2025 19:24:07 +0100 Subject: [PATCH 28/30] Fix unit hpp --- src/unit.hpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/unit.hpp b/src/unit.hpp index bac964c..722e52f 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -103,14 +103,9 @@ public: */ void calculate_movement(); + void calc_state(int posX, int posY); - /* - This function fills the MatchupTable - It would be better if this table would be placed in the level - */ - MatchupTable fill_matchupTable(int); - /* This function will be called by an external event-handler, eventually. It should start displaying standard unit information, such as UI and move_range -- GitLab From 07726a73e3c2729e799874c5aa63b83e0165d13d Mon Sep 17 00:00:00 2001 From: Lorenz <lorenz-martin.diel@informatik.hs-fulda.de> Date: Thu, 30 Jan 2025 20:56:15 +0100 Subject: [PATCH 29/30] Adjusted and refactored the function calc_state --- src/unit.cpp | 374 ++++++++++++++++++++++++--------------------------- 1 file changed, 177 insertions(+), 197 deletions(-) diff --git a/src/unit.cpp b/src/unit.cpp index d73e6b2..c938f48 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -1,207 +1,187 @@ #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), 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) - { - Spritesheet *spritesheet = engine.get_spritesheet(); - - int step = engine.get_stage() % spritesheet->get_unit_textures() - .at(static_cast<int>(faction)) - .at(static_cast<int>(id)) - .at(static_cast<int>(state)) - .second; - - if (state == UnitState::IDLE || state == UnitState::UNAVAILABLE) - { - - SDL_Rect src; - src.x = step * spritesheet->get_unit_width(); - src.y = 0; - src.w = spritesheet->get_unit_width(); - src.h = spritesheet->get_unit_height(); - - SDL_Rect dst; - dst.x = x * spritesheet->get_unit_width() * scale; - dst.y = y * spritesheet->get_unit_height() * scale; - dst.w = spritesheet->get_unit_width() * scale; - dst.h = spritesheet->get_unit_height() * scale; - - SDL_RenderCopyEx(engine.renderer(), - spritesheet->get_unit_textures() - .at(static_cast<int>(faction)) - .at(static_cast<int>(id)) - .at(static_cast<int>(state)) - .first, - &src, &dst, 0, NULL, SDL_FLIP_NONE); - } - else - { - // The moving states have a resolution of 24x24 instead of 16x16 and need to - // be handled separately - SDL_Rect src; - src.x = step * spritesheet->get_unit_moving_width(); - src.y = 0; - src.w = spritesheet->get_unit_moving_width(); - src.h = spritesheet->get_unit_moving_height(); - - SDL_Rect dst; - dst.x = ((x * spritesheet->get_unit_width()) - 4) * scale; - dst.y = ((y * spritesheet->get_unit_height()) - 4) * scale; - dst.w = spritesheet->get_unit_moving_width() * scale; - dst.h = spritesheet->get_unit_moving_height() * scale; - - SDL_RenderCopyEx(engine.renderer(), - spritesheet->get_unit_textures() - .at(static_cast<int>(faction)) - .at(static_cast<int>(id)) - .at(static_cast<int>(state)) - .first, - &src, &dst, 0, NULL, SDL_FLIP_NONE); - } - } - - 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; +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), 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) { + Spritesheet *spritesheet = engine.get_spritesheet(); + + int step = engine.get_stage() % spritesheet->get_unit_textures() + .at(static_cast<int>(faction)) + .at(static_cast<int>(id)) + .at(static_cast<int>(state)) + .second; + + if (state == UnitState::IDLE || state == UnitState::UNAVAILABLE) { + + SDL_Rect src; + src.x = step * spritesheet->get_unit_width(); + src.y = 0; + src.w = spritesheet->get_unit_width(); + src.h = spritesheet->get_unit_height(); + + SDL_Rect dst; + dst.x = x * spritesheet->get_unit_width() * scale; + dst.y = y * spritesheet->get_unit_height() * scale; + dst.w = spritesheet->get_unit_width() * scale; + dst.h = spritesheet->get_unit_height() * scale; + + SDL_RenderCopyEx(engine.renderer(), + spritesheet->get_unit_textures() + .at(static_cast<int>(faction)) + .at(static_cast<int>(id)) + .at(static_cast<int>(state)) + .first, + &src, &dst, 0, NULL, SDL_FLIP_NONE); + } else { + // The moving states have a resolution of 24x24 instead of 16x16 and need to + // be handled separately + SDL_Rect src; + src.x = step * spritesheet->get_unit_moving_width(); + src.y = 0; + src.w = spritesheet->get_unit_moving_width(); + src.h = spritesheet->get_unit_moving_height(); + + SDL_Rect dst; + dst.x = ((x * spritesheet->get_unit_width()) - 4) * scale; + dst.y = ((y * spritesheet->get_unit_height()) - 4) * scale; + dst.w = spritesheet->get_unit_moving_width() * scale; + dst.h = spritesheet->get_unit_moving_height() * scale; + + SDL_RenderCopyEx(engine.renderer(), + spritesheet->get_unit_textures() + .at(static_cast<int>(faction)) + .at(static_cast<int>(id)) + .at(static_cast<int>(state)) + .first, + &src, &dst, 0, NULL, SDL_FLIP_NONE); + } +} + +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; } - - void Unit::calc_state(int posX, int posY) { - - int currentX = this->x; - int currentY = this->y; - - int deltaX = currentX - posX; - int deltaY = currentY - posY; - - if (deltaY == 0) { - if (deltaX > 0) { - this->state = UnitState::MOVEMENTLEFT; - return; - } else { - this->state = UnitState::MOVEMENTRIGHT; - return; - } - } - - double bresen = deltaX/deltaY; - - if(bresen == 0) { - if(deltaY < 0) { - this->state = UnitState::MOVEMENTDOWN; - return; - } else { - this->state = UnitState::MOVEMENTUP; - return; - } + } + + 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::on_left_click(SDL_Event event, std::vector<Unit> &unitVector) - { - - std::cout << "Left-button pressed on unit: " << 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; } - - 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; + } 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::vector<Unit> &unitVector) { + + 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 -- GitLab From 5400f6888a1235aaff78e623c23e4744431488be Mon Sep 17 00:00:00 2001 From: Frederik <frederik@prasch.de> Date: Fri, 31 Jan 2025 00:10:44 +0100 Subject: [PATCH 30/30] Fix problems from units branch merge conflicts --- .clang-format | 187 +++++++++ CMakeLists.txt | 43 ++- README.md | 25 ++ level-spezifikation.md | 36 ++ src/assets/ARCADECLASSIC.TTF | Bin 0 -> 17296 bytes src/assets/main_background.png | Bin 0 -> 2633 bytes src/building.cpp | 37 +- src/building.hpp | 50 +-- src/effect.cpp | 41 +- src/effect.hpp | 39 +- src/engine.cpp | 144 ++++--- src/engine.hpp | 63 +-- src/level.cpp | 359 +++++++++++------ src/level.hpp | 80 ++-- src/main.cpp | 109 ++---- src/scene.hpp | 13 +- src/spritesheet.cpp | 686 ++++++++++++++++++--------------- src/spritesheet.hpp | 235 +++++++---- src/tile.cpp | 44 +-- src/tile.hpp | 81 ++-- src/ui/contextmenu.cpp | 83 ++++ src/ui/contextmenu.hpp | 34 ++ src/ui/menu.cpp | 249 ++++++++++++ src/ui/menu.hpp | 86 +++++ src/ui/pausemenu.cpp | 118 ++++++ src/ui/pausemenu.hpp | 92 +++++ src/unit.cpp | 346 +++++++++-------- src/unit.hpp | 236 ++++++------ src/weapon.cpp | 36 +- src/weapon.hpp | 30 +- src/window.cpp | 62 +-- src/window.hpp | 82 ++-- 32 files changed, 2538 insertions(+), 1188 deletions(-) create mode 100644 .clang-format create mode 100644 level-spezifikation.md create mode 100644 src/assets/ARCADECLASSIC.TTF create mode 100644 src/assets/main_background.png create mode 100644 src/ui/contextmenu.cpp create mode 100644 src/ui/contextmenu.hpp create mode 100644 src/ui/menu.cpp create mode 100644 src/ui/menu.hpp create mode 100644 src/ui/pausemenu.cpp create mode 100644 src/ui/pausemenu.hpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2910389 --- /dev/null +++ b/.clang-format @@ -0,0 +1,187 @@ +--- +BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: AlwaysBreak +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: Consecutive +AlignConsecutiveMacros: None +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAfterJavaFieldAnnotations: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Allman +BreakBeforeConceptDeclarations: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: "^ IWYU pragma:" +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: ^"(llvm|llvm-c|clang|clang-c)/ + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: ^(<|"(gtest|gmock|isl|json)/) + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: .* + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: (Test)?$ +IncludeIsMainSourceRegex: "" +IndentAccessModifiers: true +IndentCaseBlocks: true +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +Language: Cpp +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PPIndentWidth: -1 +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDeclarationName: false + AfterFunctionDefinitionName: false + AfterIfMacros: true + AfterOverloadedOperator: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME diff --git a/CMakeLists.txt b/CMakeLists.txt index c2881f5..593dc28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,23 @@ set(HIGHFIVE_UNIT_TESTS OFF) FetchContent_MakeAvailable(highfive) +FetchContent_Declare( + box2d + GIT_REPOSITORY https://github.com/erincatto/box2d.git + GIT_TAG v3.0.0 +) + +# Box2D Build-Optionen konfigurieren +set(BOX2D_BUILD_TESTBED OFF CACHE BOOL "" FORCE) +set(BOX2D_BUILD_UNIT_TESTS OFF CACHE BOOL "" FORCE) +set(BOX2D_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + +FetchContent_MakeAvailable(box2d) +set(Boost_USE_STATIC_LIBS OFF) +set(Boost_USE_MULTITHREADED ON) +set(Boost_USE_STATIC_RUNTIME OFF) +find_package(Boost 1.71.0 REQUIRED COMPONENTS graph) # Quellen sammeln file(GLOB_RECURSE ADVANCED_WARS_SOURCES @@ -31,6 +47,12 @@ file(GLOB_RECURSE ADVANCED_WARS_SOURCES set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_BUILD_TYPE Debug) + +set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra -Wpedantic") +set(CMAKE_C_FLAGS_DEBUG "-g -O0 -Wall -Wextra -Wpedantic") + +add_definitions(-DDEBUG) # Compiler-Warnungen aktivieren if(MSVC) @@ -43,7 +65,8 @@ endif() set(ASSETS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/assets) set(OUTPUT_ASSETS_DIR ${CMAKE_CURRENT_BINARY_DIR}/assets) file(MAKE_DIRECTORY ${OUTPUT_ASSETS_DIR}) -file(GLOB FONT_FILES ${ASSETS_DIR}/*.ttf) +file(GLOB FONT_FILES ${ASSETS_DIR}/*.TTF) +file(GLOB IMAGE_FILES ${ASSETS_DIR}/*.png) # Executable erstellen add_executable(advanced_wars ${ADVANCED_WARS_SOURCES}) @@ -51,6 +74,7 @@ add_executable(advanced_wars ${ADVANCED_WARS_SOURCES}) target_include_directories(advanced_wars PRIVATE ${highfive_SOURCE_DIR}/include + ${Boost_INCLUDE_DIRS} ) foreach(FONT ${FONT_FILES}) @@ -61,6 +85,14 @@ foreach(FONT ${FONT_FILES}) ) endforeach() +foreach(IMAGE ${IMAGE_FILES}) + add_custom_command( + TARGET advanced_wars PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${IMAGE} ${OUTPUT_ASSETS_DIR} + COMMENT "Kopiere Image: ${IMAGE} nach ${OUTPUT_ASSETS_DIR}" + ) +endforeach() + set(CMAKE_MODULE_PATH ${ADVANCED_WARS_SOURCE_DIR}/cmake/ ${CMAKE_MODULE_PATH}) # Plattform-spezifische Konfiguration @@ -80,12 +112,14 @@ if(APPLE) ${SDL2_PATH}/SDL2.framework/SDL2 ${SDL2_PATH}/SDL2_image.framework/SDL2_image ${SDL2_PATH}/SDL2_ttf.framework/SDL2_ttf + Boost::graph + box2d ) - # Debug-Ausgaben message(STATUS "Include Dir (SDL2): ${SDL2_PATH}/SDL2.framework/Headers") message(STATUS "Include Dir (SDL2_image): ${SDL2_PATH}/SDL2_image.framework/Headers") message(STATUS "Include Dir (SDL2_ttf): ${SDL2_PATH}/SDL2_ttf.framework/Headers") + message(STATUS "Boost Include Dirs: ${Boost_INCLUDE_DIRS}") else() find_package(SDL2 REQUIRED) find_package(SDL2_IMAGE REQUIRED) @@ -104,9 +138,8 @@ else() -lSDL2 -lSDL2_image -lSDL2_ttf + Boost::graph + box2d m ) - - endif() - diff --git a/README.md b/README.md index 8e5bcd0..e464b3a 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,31 @@ 4. Visual Studio erkennt automatisch das CMake-Projekt 5. Build über "Build All" ausführen +#### Falls Syntax errors + +1. Erstelle .vscode/c_cpp_properties.json Datei +2. Füge die folgende JSON so oder so ähnlich ein: + +```json +{ + "configurations": [ + { + "name": "Fedora", + "includePath": [ + "/usr/include", + "/usr/include/SDL2" + ], + "defines": [], + "intelliSenseMode": "linux-gcc-x64", + "compilerPath": "/usr/bin/gcc", + "cStandard": "c17", + "cppStandard": "c++17" + } + ], + "version": 4 +} +``` + ## Build-Optionen CMake kann mit verschiedenen Optionen konfiguriert werden: diff --git a/level-spezifikation.md b/level-spezifikation.md new file mode 100644 index 0000000..1f087e5 --- /dev/null +++ b/level-spezifikation.md @@ -0,0 +1,36 @@ +# Levelspezifikation + +Das Level wird ueber 2 Datasets in der HDF5-Datei repraesentiert. +Ein Dataset in XML Format, das die Level Metadaten (Breite, Hoehe, Dataset ID Tiles Array) angibt. +Ein weiters Dataset, das letztlich ein array<uint8_t> ist, welches die Levelmap ueber ein Array von Tile IDs definiert. + +## 1. XML Dataset mit Level Metadaten +```xml +<?xml version="1.0" encoding="ASCII"?> +<level> + <width>20</width> <!-- Breite des Levels --> + <height>20</height> <!-- Hoehe des Levels --> + <name>Geiles Level<name><!-- Name des Levels --> +</level> +``` + +## 2. Tiles Array +Das Tiles Array wird als array<uint8_t> in einem Dataset in der HDF5-Datei gespeichert. +Die Laenge des Arrays ist Breite X Hoehe (kann man aus XML Leveldefinition entnehmen). +Aus den einzelnen Werte in dem Array lassen sich die IDs der entsprechenden Gebaudes/Terrains ableiten. +0 - 29 sind Terrain IDs => Zuordnung siehe entsprechendes Enum in Tile.hpp +30-49 sind undefiniert +50-79 sind Gebaeude IDs: +50 => Faction ID 0, Gebaeude ID 0 +51 => Faction ID 0, Gebaeude ID 1 +... +55 => Faction ID 1, Gebaeude ID 0 +56 => Faction ID 1, Gebaeude ID 1 +57 => Faction ID 1, Gebaeude ID 2 +... +Allgemein: +Sei t ein Wert im Tiles Array, dann gillt +falls t < 30: Terrain ID = t +falls t >= 50: Faction ID = (t - 50) / 5 Gebaeude ID = (t - 50) % 5 +t wird ermittelt mit entweder t = Terrain ID fuer Terrains +oder t = 50 + 5*Faction Id + Gebaeude ID fuer Gebaeude \ No newline at end of file diff --git a/src/assets/ARCADECLASSIC.TTF b/src/assets/ARCADECLASSIC.TTF new file mode 100644 index 0000000000000000000000000000000000000000..394a9f781cedaa283a11b3b8b43c9006f4e5bac7 GIT binary patch literal 17296 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 RaA4qKU|^_ZcnVr^0|2fRI_>}f literal 0 HcmV?d00001 diff --git a/src/assets/main_background.png b/src/assets/main_background.png new file mode 100644 index 0000000000000000000000000000000000000000..5958dc0d0a7144697dece04db5dc2a1646088207 GIT binary patch literal 2633 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 literal 0 HcmV?d00001 diff --git a/src/building.cpp b/src/building.cpp index b4af7f8..c6b47dc 100644 --- a/src/building.cpp +++ b/src/building.cpp @@ -1,30 +1,31 @@ #include "building.hpp" #include "spritesheet.hpp" -namespace advanced_wars { +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) { - Spritesheet *spritesheet = engine.get_spritesheet(); +void Building::render(Engine* engine, int scale) +{ + Spritesheet* spritesheet = engine->get_spritesheet(); - SDL_Rect src; - src.x = static_cast<int>(id) * spritesheet->get_building_width(); - src.y = 0; - src.w = spritesheet->get_building_width(); - src.h = spritesheet->get_building_height(); + SDL_Rect src; + src.x = static_cast<int>(id) * spritesheet->get_building_width(); + src.y = 0; + src.w = spritesheet->get_building_width(); + src.h = spritesheet->get_building_height(); - SDL_Rect dst; - dst.x = x * spritesheet->get_tile_width() * scale; - dst.y = (y - 1) * spritesheet->get_tile_height() * scale; - dst.w = spritesheet->get_building_width() * scale; - dst.h = spritesheet->get_building_height() * scale; + SDL_Rect dst; + dst.x = x * spritesheet->get_tile_width() * scale; + dst.y = (y - 1) * spritesheet->get_tile_height() * scale; + dst.w = spritesheet->get_building_width() * scale; + dst.h = spritesheet->get_building_height() * scale; - SDL_RenderCopyEx( - engine.renderer(), - spritesheet->get_building_textures()[static_cast<int>(faction)], &src, - &dst, 0, NULL, SDL_FLIP_NONE); + SDL_RenderCopyEx( + engine->renderer(), spritesheet->get_building_textures()[static_cast<int>(faction)], &src, + &dst, 0, NULL, SDL_FLIP_NONE); } } // namespace advanced_wars \ No newline at end of file diff --git a/src/building.hpp b/src/building.hpp index d83597f..fa7a39d 100644 --- a/src/building.hpp +++ b/src/building.hpp @@ -3,35 +3,39 @@ #include "engine.hpp" #include "scene.hpp" -namespace advanced_wars { - -enum class BuildingFaction { - RED = 0, - BLUE = 1, - YELLOW = 2, - GREEN = 3, - PURPLE = 4, - NEUTRAL = 5, +namespace advanced_wars +{ + +enum class BuildingFaction +{ + RED = 0, + BLUE = 1, + YELLOW = 2, + GREEN = 3, + PURPLE = 4, + NEUTRAL = 5, }; -enum class BuildingId { - HEADQUARTER = 0, - CITY = 1, - FACTORY = 2, - PORT = 3, - SATELLITE = 4, +enum class BuildingId +{ + HEADQUARTER = 0, + CITY = 1, + FACTORY = 2, + PORT = 3, + SATELLITE = 4, }; -class Building { -public: - Building(int x, int y, BuildingId id, BuildingFaction faction); +class Building +{ + public: + Building(int x, int y, BuildingId id, BuildingFaction faction); - int x; - int y; - BuildingId id; - BuildingFaction faction; + int x; + int y; + BuildingId id; + BuildingFaction faction; - void render(Engine &engine, int scale); + void render(Engine* engine, int scale); }; } // namespace advanced_wars \ No newline at end of file diff --git a/src/effect.cpp b/src/effect.cpp index c0e22d6..f3ab594 100644 --- a/src/effect.cpp +++ b/src/effect.cpp @@ -2,28 +2,27 @@ #include "spritesheet.hpp" #include <vector> -namespace advanced_wars { +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) { - Spritesheet *spritesheet = engine.get_spritesheet(); - if (start == 0) { - start = engine.get_stage(); - } +void Effect::render(Engine* engine, int scale) +{ + Spritesheet* spritesheet = engine->get_spritesheet(); + if (start == 0) + { + start = engine->get_stage(); + } - int step = engine.get_stage() % - spritesheet->get_effect_textures().at(static_cast<int>(id)).second; + int step = + engine->get_stage() % spritesheet->get_effect_textures().at(static_cast<int>(id)).second; - if (engine.get_stage() - start <= - spritesheet->get_effect_textures().at(static_cast<int>(id)).second || - repeat) { SDL_Rect src; - src.x = step * spritesheet->get_effect_width() + - step * spritesheet->get_effect_height(); + src.x = step * spritesheet->get_effect_width() + step * spritesheet->get_effect_height(); src.y = 0; src.w = spritesheet->get_effect_width(); src.h = spritesheet->get_effect_height(); @@ -35,10 +34,16 @@ void Effect::render(Engine &engine, int scale) { dest.h = spritesheet->get_effect_height() * scale; SDL_RenderCopyEx( - engine.renderer(), - spritesheet->get_effect_textures().at(static_cast<int>(id)).first, &src, + engine->renderer(), spritesheet->get_effect_textures().at(static_cast<int>(id)).first, &src, &dest, 0, NULL, SDL_FLIP_NONE); - } +} + +bool Effect::is_finished(Engine* engine) +{ + return !( + engine->get_stage() - start <= + engine->get_spritesheet()->get_effect_textures().at(static_cast<int>(id)).second || + repeat); } } // namespace advanced_wars \ No newline at end of file diff --git a/src/effect.hpp b/src/effect.hpp index e5b94a1..f33923d 100644 --- a/src/effect.hpp +++ b/src/effect.hpp @@ -2,27 +2,32 @@ #include "engine.hpp" -namespace advanced_wars { - -enum class EffectId { - LAND_EXPLOSION = 0, - AIR_EXPLOSION = 1, - NAVAL_EXPLOSION = 2, - SUBMARINE_HIDE = 3, - SUBMARINE_APPEAR = 4 +namespace advanced_wars +{ + +enum class EffectId +{ + LAND_EXPLOSION = 0, + AIR_EXPLOSION = 1, + NAVAL_EXPLOSION = 2, + SUBMARINE_HIDE = 3, + SUBMARINE_APPEAR = 4 }; -class Effect { -public: - Effect(int x, int y, EffectId id, bool repeat); +class Effect +{ + public: + Effect(int x, int y, EffectId id, bool repeat); + + void render(Engine* engine, int scale); - void render(Engine &engine, int scale); + bool is_finished(Engine* engine); - int x; - int y; - EffectId id; - bool repeat; - int start; + int x; + int y; + EffectId id; + bool repeat; + int start; }; } // namespace advanced_wars \ No newline at end of file diff --git a/src/engine.cpp b/src/engine.cpp index e5f9b8e..f7c58d6 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -1,85 +1,129 @@ #include "engine.hpp" +#include "SDL_events.h" +#include "SDL_timer.h" #include "scene.hpp" #include "spritesheet.hpp" #include "window.hpp" #include <SDL.h> #include <SDL_image.h> #include <SDL_render.h> +#include <deque> +#include <memory> +#include <optional> #include <stdexcept> -#include <vector> -namespace advanced_wars { +namespace advanced_wars +{ -Engine::Engine(Window &window) : window(window), quit(false) { +Engine::Engine(Window& window) : window(window), quit(false) +{ - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - throw std::runtime_error("SDL could not initialize: " + - std::string(SDL_GetError())); - } + this->sdl_renderer = SDL_CreateRenderer( + this->window.sdl_window(), -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - int imgFlags = IMG_INIT_PNG; - if (!(IMG_Init(imgFlags) & imgFlags)) { - throw std::runtime_error( - "SDL_image could not initialize! SDL_image Error: " + - std::string(IMG_GetError())); - } + if (sdl_renderer == nullptr) + { + throw std::runtime_error("SDL could not generate renderer: " + std::string(SDL_GetError())); + } +} + +std::deque<SDL_Event>& Engine::events() +{ + return this->_events; +} - this->sdl_renderer = - SDL_CreateRenderer(this->window.sdl_window(), -1, - SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); +void Engine::push_scene(std::shared_ptr<Scene> scene) +{ + this->scenes.push_back(scene); +} - if (sdl_renderer == nullptr) { - throw std::runtime_error("SDL could not generate renderer: " + - std::string(SDL_GetError())); - } +void Engine::return_to_menu() +{ + // TODO: discuss if we outsource this to a separate function + // clear everything except the first scene + while (this->scenes.size() > 1) + { + this->scenes.pop_back(); + } } -void Engine::set_scene(Scene &scene) { this->scene = &scene; } +std::optional<std::shared_ptr<Scene>> Engine::pop_scene() +{ + if (this->scenes.empty()) + { + return std::nullopt; + } + std::shared_ptr<Scene> tmp = scenes.back(); + this->scenes.pop_back(); -void Engine::set_spritesheet(Spritesheet &spritesheet) { - this->spritesheet = &spritesheet; + return tmp; } -Spritesheet *Engine::get_spritesheet() { return spritesheet.value(); } +void Engine::set_spritesheet(Spritesheet& spritesheet) +{ + this->spritesheet = &spritesheet; +} -void Engine::pump() { - SDL_Event e; - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - this->quit = true; - } else { - this->events.push_back(e); +void Engine::pump() +{ + SDL_Event e; + while (SDL_PollEvent(&e)) + { + if (e.type == SDL_QUIT) + { + this->quit = true; + } + else + { + this->_events.push_back(e); + } } - } } -bool Engine::exited() { return this->quit; } +void Engine::exit() +{ + this->quit = true; +} -int Engine::get_stage() { return this->stage; } +bool Engine::exited() +{ + return this->quit; +} -void Engine::render() { - if (SDL_RenderClear(this->sdl_renderer) != 0) { - throw std::runtime_error("Could not clear renderer: " + - std::string(SDL_GetError())); - } +void Engine::render() +{ + if (SDL_RenderClear(this->sdl_renderer) != 0) + { + throw std::runtime_error("Could not clear renderer: " + std::string(SDL_GetError())); + } - if (!scene.has_value()) { - return; - } + std::shared_ptr<Scene> currentScene = scenes.back(); - stage = SDL_GetTicks() / 300; + currentScene->render(this); - this->scene.value()->render(*this, this->events); + SDL_RenderPresent(this->sdl_renderer); +} - SDL_RenderPresent(this->sdl_renderer); +int Engine::get_stage() +{ + return SDL_GetTicks() / 300; } -SDL_Renderer *Engine::renderer() { return this->sdl_renderer; } +Spritesheet* Engine::get_spritesheet() +{ + return spritesheet.value(); +} + +SDL_Renderer* Engine::renderer() +{ + return this->sdl_renderer; +} -Engine::~Engine() { - SDL_DestroyRenderer(sdl_renderer); - IMG_Quit(); - SDL_Quit(); +Engine::~Engine() +{ + SDL_DestroyRenderer(sdl_renderer); + IMG_Quit(); + SDL_Quit(); } } // namespace advanced_wars diff --git a/src/engine.hpp b/src/engine.hpp index 827d05d..4b247e8 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -1,51 +1,66 @@ #pragma once +#include "SDL_events.h" #include "scene.hpp" #include "spritesheet.hpp" #include "window.hpp" #include <SDL.h> #include <SDL_render.h> +#include <deque> +#include <memory> #include <optional> -#include <vector> -namespace advanced_wars { +namespace advanced_wars +{ + +// Forward declaration +class Scene; /** * @brief The main window of the game */ -class Engine { -public: - Engine(Window &window); +class Engine +{ + public: + Engine(Window& window); + + Engine(const Engine&) = delete; + Engine& operator=(const Engine&) = delete; + + bool exited(); + + void exit(); + + void pump(); - Engine(const Engine &) = delete; - Engine &operator=(const Engine &) = delete; + void push_scene(std::shared_ptr<Scene> scene); - bool exited(); + std::optional<std::shared_ptr<Scene>> pop_scene(); - void pump(); + void return_to_menu(); - void set_scene(Scene &scene); + std::deque<SDL_Event>& events(); - void set_spritesheet(Spritesheet &spritesheet); + void set_spritesheet(Spritesheet& spritesheet); - Spritesheet *get_spritesheet(); + Spritesheet* get_spritesheet(); - int get_stage(); + int get_stage(); - void render(); + void render(); - SDL_Renderer *renderer(); + SDL_Renderer* renderer(); - ~Engine(); + ~Engine(); -private: - Window &window; - SDL_Renderer *sdl_renderer; - std::optional<Scene *> scene; - std::optional<Spritesheet *> spritesheet; - std::vector<SDL_Event> events; - bool quit; - int stage; + private: + Window& window; + SDL_Renderer* sdl_renderer; + std::vector<std::shared_ptr<Scene>> scenes; + std::optional<Spritesheet*> spritesheet; + std::deque<SDL_Event> _events; + bool quit; + int stage; }; } // namespace advanced_wars diff --git a/src/level.cpp b/src/level.cpp index 154a2ea..927ca47 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -1,225 +1,342 @@ #include "level.hpp" -#include "SDL_error.h" #include "building.hpp" #include "effect.hpp" #include "engine.hpp" #include "spritesheet.hpp" +#include "ui/contextmenu.hpp" +#include "ui/pausemenu.hpp" #include "unit.hpp" #include <SDL.h> +#include <algorithm> #include <iostream> #include <string> -#include <algorithm> namespace advanced_wars { - Level::Level(std::string name, int width, int height, std::vector<Tile> tiles, - std::vector<Building> buildings, std::vector<Unit> units, - std::vector<Effect> effects) - : name(name), width(width), height(height), tiles(tiles), - buildings(buildings), units(units), effects(effects) - { +Level::Level( + std::string name, int width, int height, std::vector<Tile> tiles, + std::vector<Building> buildings, std::vector<Unit> units, std::vector<Effect> effects) + : name(name), width(width), height(height), tiles(tiles), context_menu(ContextMenu()), + context_menu_active(false), id(0) +{ + + context_menu.setOptions({"Move", "Info", "Wait"}); + + for (Building building : buildings) + { + this->add_building(building); + } + + for (Unit unit : units) + { + this->add_unit(unit); + } + + for (Effect effect : effects) + { + this->add_effect(effect); + } if ((size_t)(width * height) != tiles.size()) { - throw std::runtime_error("level tile mismatch"); + throw std::runtime_error("level tile mismatch"); } - }; +}; - const int RENDERING_SCALE = 3; +const int RENDERING_SCALE = 3; - bool Level::click_check_left(int tileX, int tileY) - { +bool Level::click_check_left(int tileX, int tileY) +{ if (selectUnit(tileX, tileY)) { - return true; + return true; } if (selectBuilding(tileX, tileY)) { - return true; + return true; } return false; - } +} - bool Level::click_check_right(int tileX, int tileY) - { +bool Level::click_check_right(int tileX, int tileY) +{ if (target_unit(tileX, tileY)) { - return true; + return true; } return false; - } +} - bool Level::selectUnit(int tileX, int tileY) - { +bool Level::selectUnit(int tileX, int tileY) +{ // std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; - for (auto &unit : units) + for (auto& [id, unit] : units) { - if (unit.x == tileX && unit.y == tileY) - { - // std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; + if (unit.x == tileX && unit.y == tileY) + { + // std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; - selectedUnit = &unit; - return true; - } + selectedUnit = id; + return true; + } } return false; - } +} - bool Level::target_unit(int tileX, int tileY) - { +bool Level::target_unit(int tileX, int tileY) +{ // std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl; - for (auto &unit : units) + for (auto& [id, unit] : units) { - if (unit.x == tileX && unit.y == tileY) - { - // std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; + if (unit.x == tileX && unit.y == tileY) + { + // std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl; - targetedUnit = &unit; - return true; - } + targetedUnit = id; + return true; + } } return false; - } +} - bool Level::selectBuilding(int tileX, int tileY) - { +bool Level::selectBuilding(int tileX, int tileY) +{ - for (auto &building : buildings) + for (auto& [id, building] : buildings) { - if (building.x == tileX && building.y == tileY) - { - // std::cout << "X:" << unit.x << "Y:" << unit.y << std::endl; - selectedBuilding = &building; - return true; - } + 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) - { +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 (event.button.button == SDL_BUTTON_LEFT) { - if (selectedUnit) - { - selectedUnit->on_left_click(event, units); - } - - if (selectedBuilding) - { - // building stuff - } + 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 + else if (event.button.button == SDL_BUTTON_RIGHT) { - std::cout << "Neither building nor unit clicked!" << std::endl; - selectedUnit = nullptr; - selectedBuilding = nullptr; - } - } - else if (event.button.button == SDL_BUTTON_RIGHT) - { - - if (selectedUnit) - { + if (selectedUnit > -1) + { - int tileX = event.button.x / (16 * RENDERING_SCALE); - int tileY = event.button.y / (16 * RENDERING_SCALE); + int tileX = event.button.x / (16 * RENDERING_SCALE); + int tileY = event.button.y / (16 * RENDERING_SCALE); - if (click_check_right(tileX, tileY)) - { + if (click_check_right(tileX, tileY)) + { - selectedUnit->attack(targetedUnit); + units.at(selectedUnit).attack(&(units.at(targetedUnit))); - units.erase( - std::remove_if(units.begin(), units.end(), - [](const Unit &unit) - { return unit.health <= 0; }), - units.end()); - } - else - { + if (units.at(selectedUnit).health <= 0) + { + remove_unit(selectedUnit); + } + } + else + { - selectedUnit->update_position(tileX, tileY); - } - } - else - { + units.at(selectedUnit).update_position(tileX, tileY); + } + } + else + { - std::cout << "No unit selected! " << std::endl; + std::cout << "No unit selected! " << std::endl; + } } - } } - } +} - void Level::render(Engine &engine, std::vector<SDL_Event> &events) - { +void Level::render(Engine* engine) +{ // Iterate over all events - while (!events.empty()) + while (!engine->events().empty()) { - // events.erase(events.begin()); - - handleEvent(engine, events.at(0)); - events.erase(events.begin()); + // handleEvent(engine, engine->events().at(0)); + handleEvent(*engine, engine->events().at(0)); + engine->events().pop_front(); } // Tiles - for (Tile &tile : tiles) + for (Tile& tile : tiles) { - tile.render(engine, RENDERING_SCALE); + tile.render(engine, RENDERING_SCALE); } // Buildings - for (Building &building : buildings) + for (auto& [id, building] : buildings) { - building.render(engine, RENDERING_SCALE); + building.render(engine, RENDERING_SCALE); } // Units - for (Unit &unit : units) + for (auto& [id, unit] : units) { - unit.render(engine, RENDERING_SCALE); + unit.render(engine, RENDERING_SCALE); } // Effects - for (Effect &effect : effects) + std::vector<int> effects_to_remove; + for (auto& [id, effect] : effects) + { + if (effect.is_finished(engine)) + { + effects_to_remove.push_back(id); + } + else + { + effect.render(engine, RENDERING_SCALE); + } + } + + // Remove finished effects after iteration + for (int id : effects_to_remove) { - effect.render(engine, RENDERING_SCALE); + this->remove_effect(id); } - // Set background color for renderer - if (SDL_SetRenderDrawColor(engine.renderer(), 255, 0, 0, 0)) + if (context_menu_active) + { + context_menu.render(engine); + } +} + +void Level::handleEvent(Engine* engine, SDL_Event& event) +{ + // Handle events for the level + if (event.type == SDL_KEYDOWN) + { + if (event.key.keysym.sym == SDLK_ESCAPE) + { + // Pause the game + std::cout << "Pausing game..." << std::endl; + SDL_Texture* currentTexture = SDL_CreateTexture( + engine->renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 800, 600); + + PauseMenu pauseMenu(0, currentTexture); + engine->push_scene(std::make_shared<PauseMenu>(pauseMenu)); + } + if (context_menu_active) + { + if (event.key.keysym.sym == SDLK_DOWN) + { + context_menu.handleEvent(event); + } + if (event.key.keysym.sym == SDLK_UP) + { + context_menu.handleEvent(event); + } + if (event.key.keysym.sym == SDLK_RETURN) + { + if (context_menu.getSelectedOption() == "Wait") + { + context_menu_active = false; + } + } + } + } + if (event.type == SDL_MOUSEBUTTONDOWN) { - std::cout << "Could not set render draw color: " << SDL_GetError() - << std::endl; + context_menu.update(event.button.x, event.button.y); + context_menu_active = true; } - } +} + +int Level::add_building(Building building) +{ + buildings.insert({id, building}); + id += 1; + + return id - 1; +} + +Building Level::remove_building(int id) +{ + Building value = buildings.at(id); + buildings.erase(id); + + return value; +} + +int Level::add_unit(Unit unit) +{ + units.insert({id, unit}); + id += 1; + + return id - 1; +} + +Unit Level::remove_unit(int id) +{ + Unit value = units.at(id); + units.erase(id); + + return value; +} + +int Level::add_effect(Effect effect) +{ + effects.insert({id, effect}); + id += 1; + + return id - 1; +} + +Effect Level::remove_effect(int id) +{ + Effect value = effects.at(id); + effects.erase(id); + + return value; +} -} // namespace advanced_wars +} // namespace advanced_wars \ No newline at end of file diff --git a/src/level.hpp b/src/level.hpp index 7c5e99b..0750148 100644 --- a/src/level.hpp +++ b/src/level.hpp @@ -5,43 +5,67 @@ #include "engine.hpp" #include "scene.hpp" #include "tile.hpp" +#include "ui/contextmenu.hpp" #include "unit.hpp" #include <SDL.h> #include <string> +#include <unordered_map> #include <vector> -namespace advanced_wars { +namespace advanced_wars +{ /** * @brief The main window of the game */ -class Level : public Scene { -public: - Level(std::string name, int width, int height, std::vector<Tile> tiles, - std::vector<Building> buildings, std::vector<Unit> units, - std::vector<Effect>); - - void render(Engine &engine, std::vector<SDL_Event> &events); - - void handleEvent(Engine &engine, SDL_Event &event); - -private: - std::string name; - int width; - int height; - std::vector<Tile> tiles; - std::vector<Building> buildings; - std::vector<Unit> units; - std::vector<Effect> effects; - Unit* selectedUnit; - Unit* targetedUnit; - Building* 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); +class Level : public Scene +{ + public: + Level( + std::string name, int width, int height, std::vector<Tile> tiles, + std::vector<Building> buildings, std::vector<Unit> units, std::vector<Effect>); + + void render(Engine* engine); + + void handleEvent(Engine* engine, SDL_Event& event); + + int add_building(Building building); + + Building remove_building(int id); + + int add_unit(Unit unit); + + Unit remove_unit(int id); + + int add_effect(Effect effect); + + Effect remove_effect(int id); + + void handleEvent(Engine& engine, SDL_Event& event); + + private: + std::string name; + int width; + int height; + + std::vector<Tile> tiles; + 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; + + int id; }; } // namespace advanced_wars diff --git a/src/main.cpp b/src/main.cpp index a6d4dd4..3d24912 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,96 +1,55 @@ -#include "building.hpp" -#include "effect.hpp" #include "engine.hpp" -#include "level.hpp" #include "spritesheet.hpp" -#include "tile.hpp" -#include "unit.hpp" +#include "ui/contextmenu.hpp" +#include "ui/menu.hpp" #include "window.hpp" -#include <cstddef> +#include <SDL2/SDL.h> +#include <SDL_image.h> +#include <memory> +#include <stdexcept> #include <vector> using namespace advanced_wars; -int main() { +int main() +{ - Window window("Advanced Wars", 960, 960); - - Engine engine(window); - - // Construct a level - std::vector<Tile> tiles; - for (int y = 0; y < 20; y++) { - for (int x = 0; x < 20; x++) { - tiles.push_back(Tile(TileId::PLAIN, x, y)); + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + throw std::runtime_error("SDL could not initialize: " + std::string(SDL_GetError())); } - } - - // Fill the edges with water - for (size_t n = 0; n < 20; n++) { - // Vertical - tiles.at(n * 20) = Tile(TileId::WATER, 0, n); - tiles.at(n * 20 + 19) = Tile(TileId::WATER, 19, n); - // Horizontal - tiles.at(n) = Tile(TileId::WATER, n, 0); - tiles.at(19 * 20 + n) = Tile(TileId::WATER, n, 19); - } - - // Make the edges cliffs - for (size_t n = 1; n < 19; n++) { - // Vertical - tiles.at(n * 20 + 1) = Tile(TileId::CLIFF_RIGHT, 1, n); - tiles.at(n * 20 + 18) = Tile(TileId::CLIFF_LEFT, 18, n); - - // Horizontal - tiles.at(20 + n) = Tile(TileId::CLIFF_BOTTOM, n, 1); - tiles.at(18 * 20 + n) = Tile(TileId::CLIFF_TOP, n, 18); - } - // Fix the corners - tiles.at(20 + 1) = Tile(TileId::CLIFF_CORNER_TOP_LEFT, 1, 1); - tiles.at(20 + 18) = Tile(TileId::CLIFF_CORNER_TOP_RIGHT, 18, 1); - tiles.at(18 * 20 + 1) = Tile(TileId::CLIFF_CORNER_BOTTOM_LEFT, 1, 18); - tiles.at(18 * 20 + 18) = Tile(TileId::CLIFF_CORNER_BOTTOM_RIGHT, 18, 18); - - // Buildings - std::vector<Building> buildings; - - for (int y = 0; y < 6; y++) { - for (int x = 0; x < 5; x++) { - BuildingId id = static_cast<BuildingId>(x); - BuildingFaction faction = static_cast<BuildingFaction>(y); - - buildings.push_back(Building(3 + x, 3 + 2 * y, id, faction)); + int imgFlags = IMG_INIT_PNG; + if (!(IMG_Init(imgFlags) & imgFlags)) + { + throw std::runtime_error( + "SDL_image could not initialize! SDL_image Error: " + std::string(IMG_GetError())); } - } - // Units - std::vector<Unit> units; + Window window("Advanced Wars", 960, 960); - for (int y = 0; y < 19; y++) { - for (int x = 0; x < 6; x++) { - units.push_back(Unit(x + 9, y + 2, UnitFaction::URED, - static_cast<UnitId>(y), static_cast<UnitState>(x))); - } - } + Engine engine(window); - std::vector<Effect> effects({Effect(3, 15, EffectId::LAND_EXPLOSION, true), - Effect(5, 15, EffectId::AIR_EXPLOSION, true), - Effect(5, 18, EffectId::NAVAL_EXPLOSION, true)}); + Spritesheet spritesheet("/media/data/rust/sprite-extractor/spritesheet.h5", engine); - Level level("Osnabrück", 20, 20, tiles, buildings, units, effects); + engine.set_spritesheet(spritesheet); - engine.set_scene(level); + std::shared_ptr<Menu> menu = std::make_shared<Menu>(0); + std::shared_ptr<ContextMenu> context_menu = std::make_shared<ContextMenu>(); + context_menu->setOptions({"Move", "Info", "Wait"}); - Spritesheet spritesheet("./spritesheet.h5", - engine); + std::string basePath = SDL_GetBasePath(); + std::string relativePath = "assets/main_background.png"; + std::string fullPath = basePath + relativePath; + menu->loadBackground(engine.renderer(), fullPath.c_str()); - engine.set_spritesheet(spritesheet); + engine.push_scene(menu); - while (!engine.exited()) { - engine.pump(); - engine.render(); - } + while (!engine.exited()) + { + engine.pump(); + engine.render(); + } - return 0; + return 0; } diff --git a/src/scene.hpp b/src/scene.hpp index 5bd5283..011b642 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -1,15 +1,18 @@ #pragma once +#include "engine.hpp" #include <SDL.h> -#include <vector> -namespace advanced_wars { +namespace advanced_wars +{ // Forward declaration class Engine; -class Scene { -public: - virtual void render(Engine &engine, std::vector<SDL_Event> &events) = 0; +class Scene +{ + public: + virtual void render(Engine* engine) = 0; }; + } // namespace advanced_wars diff --git a/src/spritesheet.cpp b/src/spritesheet.cpp index 96a8f2a..efd7a93 100644 --- a/src/spritesheet.cpp +++ b/src/spritesheet.cpp @@ -1,5 +1,12 @@ +/** + * Spritesheet.hpp + * + * @date 30.1.2025 + * @author Frederik Keens + * @author David Maul + */ + #include "spritesheet.hpp" -#include "SDL_pixels.h" #include "engine.hpp" #include "highfive/H5File.hpp" #include <SDL_image.h> @@ -10,377 +17,446 @@ #include <string> #include <vector> -namespace advanced_wars { - -Spritesheet::Spritesheet(std::string path, Engine &engine) { - - HighFive::File file(path, HighFive::File::ReadOnly); - - // Tiles - std::vector<std::string> tiles({"plain", - "water", - "forest", - "mountain", - "bridge_horizontal", - "bridge_vertical", - "street_horizontal", - "street_vertical", - "street_crossing", - "street_junction_right", - "street_junction_left", - "street_junction_down", - "street_junction_up", - "street_corner_top_left", - "street_corner_top_right", - "street_corner_bottom_left", - "street_corner_bottom_right", - "riff", - "cliff_top", - "cliff_bottom", - "cliff_left", - "cliff_right", - "cliff_corner_top_left", - "cliff_corner_top_right", - "cliff_corner_bottom_left", - "cliff_corner_bottom_right", - "cliff_inverse_corner_top_left", - "cliff_inverse_corner_top_right", - "cliff_inverse_corner_bottom_left", - "cliff_inverse_corner_bottom_right"}); - - for (size_t tile_idx = 0; tile_idx < tiles.size(); tile_idx++) { - HighFive::DataSet units_ds = file.getDataSet("tiles/" + tiles[tile_idx]); - - std::vector<std::vector<std::vector<uint32_t>>> tile_frames; - units_ds.read(tile_frames); - - std::vector<uint32_t> tile_buffer(16 * 16 * tile_frames.size(), 0); - - for (size_t n = 0; n < tile_frames.size(); n++) { - for (size_t y = 0; y < 16; y++) { - for (size_t x = 0; x < 16; x++) { - size_t index = (y * tile_frames.size() * 16) + (n * 16 + x); - - tile_buffer.at(index) = tile_frames.at(n).at(16 - y - 1).at(x); +namespace advanced_wars +{ + +Spritesheet::Spritesheet(std::string path, Engine& engine) +{ + + HighFive::File file(path, HighFive::File::ReadOnly); + + // Tiles + std::vector<std::string> tiles( + {"plain", + "water", + "forest", + "mountain", + "bridge_horizontal", + "bridge_vertical", + "street_horizontal", + "street_vertical", + "street_crossing", + "street_junction_right", + "street_junction_left", + "street_junction_down", + "street_junction_up", + "street_corner_top_left", + "street_corner_top_right", + "street_corner_bottom_left", + "street_corner_bottom_right", + "riff", + "cliff_top", + "cliff_bottom", + "cliff_left", + "cliff_right", + "cliff_corner_top_left", + "cliff_corner_top_right", + "cliff_corner_bottom_left", + "cliff_corner_bottom_right", + "cliff_inverse_corner_top_left", + "cliff_inverse_corner_top_right", + "cliff_inverse_corner_bottom_left", + "cliff_inverse_corner_bottom_right"}); + + // every sub data set of tiles + for (size_t tile_idx = 0; tile_idx < tiles.size(); tile_idx++) + { + HighFive::DataSet units_ds = file.getDataSet("tiles/" + tiles[tile_idx]); + + std::vector<std::vector<std::vector<uint32_t>>> tile_frames; + units_ds.read(tile_frames); + + std::vector<uint32_t> tile_buffer(16 * 16 * tile_frames.size(), 0); + + // every animation frame + for (size_t n = 0; n < tile_frames.size(); n++) + { + for (size_t y = 0; y < 16; y++) + { + for (size_t x = 0; x < 16; x++) + { + size_t index = (y * tile_frames.size() * 16) + (n * 16 + x); + + tile_buffer.at(index) = tile_frames.at(n).at(16 - y - 1).at(x); + } + } } - } - } - - SDL_Texture *tmp = SDL_CreateTexture( - engine.renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, - tile_frames.size() * 16, 16); - - SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND); - - if (tmp == nullptr) { - throw std::runtime_error( - "Fehler beim Erstellen der Textur für die Units: " + - std::string(SDL_GetError())); - } - - if (SDL_UpdateTexture(tmp, NULL, tile_buffer.data(), - tile_frames.size() * 16 * sizeof(int32_t)) != 0) { - throw std::runtime_error( - "Fehler beim updaten der Textur für die Units: " + - std::string(SDL_GetError())); - } - tile_textures.push_back( - std::pair<SDL_Texture *, int>(tmp, tile_frames.size())); - } + SDL_Texture* tmp = SDL_CreateTexture( + engine.renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, + tile_frames.size() * 16, 16); - this->tile_width = 16; - this->tile_height = 16; - - // Buildings - std::vector<std::string> building_factions( - {"red", "blue", "yellow", "green", "purple", "neutral"}); - - for (std::string faction : building_factions) { - HighFive::DataSet buildings_ds = file.getDataSet("buildings/" + faction); - - std::vector<std::vector<std::vector<uint32_t>>> buildings_frames; - - buildings_ds.read(buildings_frames); - - std::vector<uint32_t> building_buffer(32 * 16 * buildings_frames.size(), 0); - - for (size_t n = 0; n < buildings_frames.size(); n++) { - for (size_t y = 0; y < 32; y++) { - for (size_t x = 0; x < 16; x++) { - size_t index = (y * buildings_frames.size() * 16) + (n * 16 + x); + SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND); - building_buffer.at(index) = - buildings_frames.at(n).at(32 - y - 1).at(x); + if (tmp == nullptr) + { + throw std::runtime_error( + "Fehler beim Erstellen der Textur für die Units: " + std::string(SDL_GetError())); } - } - } - SDL_Texture *tmp = SDL_CreateTexture( - engine.renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, - buildings_frames.size() * 16, 32); - - SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND); - - if (tmp == nullptr) { - throw std::runtime_error( - "Fehler beim Erstellen der Textur für die Buildings: " + - std::string(SDL_GetError())); - } + if (SDL_UpdateTexture( + tmp, NULL, tile_buffer.data(), tile_frames.size() * 16 * sizeof(int32_t)) != 0) + { + throw std::runtime_error( + "Fehler beim updaten der Textur für die Units: " + std::string(SDL_GetError())); + } - if (SDL_UpdateTexture(tmp, NULL, building_buffer.data(), - buildings_frames.size() * 16 * sizeof(int32_t)) != - 0) { - throw std::runtime_error( - "Fehler beim updaten der Textur für die Buildings: " + - std::string(SDL_GetError())); + tile_textures.push_back(std::pair<SDL_Texture*, int>(tmp, tile_frames.size())); } - this->building_textures.push_back(tmp); - } - - this->building_width = 16; - this->building_height = 32; - - // Units - std::vector<std::string> unit_factions( - {"red", "blue", "green", "yellow", "purple"}); - - std::vector<std::string> units( - {"infantery", "mechanized_infantery", "recon", "medium_tank", - "heavy_tank", "neo_tank", "apc", "anti_air_tank", "artillery", - "rocket_artillery", "anti_air_missile_launcher", "fighter", "bomber", - "battle_helicopter", "transport_helicopter", "battleship", "cruiser", - "lander", "submarine"}); - - std::vector<std::string> unit_states({"idle", "unavailable"}); - std::vector<std::string> unit_movement_states( - {"left", "right", "down", "up"}); - - for (size_t faction_idx = 0; faction_idx < unit_factions.size(); - faction_idx++) { - std::string faction = unit_factions.at(faction_idx); - // Create entry for units for in a faction - unit_textures.push_back( - std::vector<std::vector<std::pair<SDL_Texture *, int>>>()); + this->tile_width = 16; + this->tile_height = 16; - for (size_t unit_idx = 0; unit_idx < units.size(); unit_idx++) { - std::string unit = units.at(unit_idx); + // Buildings + std::vector<std::string> building_factions( + {"red", "blue", "yellow", "green", "purple", "neutral"}); - // Create entry for states for a unit - unit_textures.at(faction_idx) - .push_back(std::vector<std::pair<SDL_Texture *, int>>()); + // every sub data set of buildings + for (std::string faction : building_factions) + { + HighFive::DataSet buildings_ds = file.getDataSet("buildings/" + faction); - for (size_t state_idx = 0; state_idx < unit_states.size(); state_idx++) { - std::string unit_state = unit_states.at(state_idx); + std::vector<std::vector<std::vector<uint32_t>>> buildings_frames; - HighFive::DataSet units_ds = - file.getDataSet("units/" + faction + "/" + unit + "/" + unit_state); + buildings_ds.read(buildings_frames); - std::vector<std::vector<std::vector<uint32_t>>> unit_frames; - units_ds.read(unit_frames); + std::vector<uint32_t> building_buffer(32 * 16 * buildings_frames.size(), 0); - std::vector<uint32_t> unit_buffer(16 * 16 * unit_frames.size(), 0); + // every type of building + for (size_t n = 0; n < buildings_frames.size(); n++) + { + for (size_t y = 0; y < 32; y++) + { + for (size_t x = 0; x < 16; x++) + { + size_t index = (y * buildings_frames.size() * 16) + (n * 16 + x); - for (size_t n = 0; n < unit_frames.size(); n++) { - for (size_t y = 0; y < 16; y++) { - for (size_t x = 0; x < 16; x++) { - size_t index = (y * unit_frames.size() * 16) + (n * 16 + x); - - unit_buffer.at(index) = unit_frames.at(n).at(16 - y - 1).at(x); + building_buffer.at(index) = buildings_frames.at(n).at(32 - y - 1).at(x); + } } - } } - SDL_Texture *tmp = SDL_CreateTexture( - engine.renderer(), SDL_PIXELFORMAT_RGBA8888, - SDL_TEXTUREACCESS_STATIC, unit_frames.size() * 16, 16); + SDL_Texture* tmp = SDL_CreateTexture( + engine.renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, + buildings_frames.size() * 16, 32); SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND); - if (tmp == nullptr) { - throw std::runtime_error( - "Fehler beim Erstellen der Textur für die Units: " + - std::string(SDL_GetError())); + if (tmp == nullptr) + { + throw std::runtime_error( + "Fehler beim Erstellen der Textur für die Buildings: " + + std::string(SDL_GetError())); } - if (SDL_UpdateTexture(tmp, NULL, unit_buffer.data(), - unit_frames.size() * 16 * sizeof(int32_t)) != 0) { - throw std::runtime_error( - "Fehler beim updaten der Textur für die Units: " + - std::string(SDL_GetError())); + if (SDL_UpdateTexture( + tmp, NULL, building_buffer.data(), + buildings_frames.size() * 16 * sizeof(int32_t)) != 0) + { + throw std::runtime_error( + "Fehler beim updaten der Textur für die Buildings: " + std::string(SDL_GetError())); } - unit_textures.at(faction_idx) - .at(unit_idx) - .push_back(std::pair<SDL_Texture *, int>(tmp, unit_frames.size())); - } - - for (size_t movement_state_idx = 0; - movement_state_idx < unit_movement_states.size(); - movement_state_idx++) { - std::string movement_state = - unit_movement_states.at(movement_state_idx); - - HighFive::DataSet units_ds = file.getDataSet( - "units/" + faction + "/" + unit + "/movement/" + movement_state); - - std::vector<std::vector<std::vector<uint32_t>>> unit_frames; - units_ds.read(unit_frames); + this->building_textures.push_back(tmp); + } - std::vector<uint32_t> unit_buffer(24 * 24 * unit_frames.size(), 0); + this->building_width = 16; + this->building_height = 32; + + // Units + std::vector<std::string> unit_factions({"red", "blue", "green", "yellow", "purple"}); + + std::vector<std::string> units( + {"infantery", "mechanized_infantery", "recon", "medium_tank", "heavy_tank", "neo_tank", + "apc", "anti_air_tank", "artillery", "rocket_artillery", "anti_air_missile_launcher", + "fighter", "bomber", "battle_helicopter", "transport_helicopter", "battleship", "cruiser", + "lander", "submarine"}); + + std::vector<std::string> unit_states({"idle", "unavailable"}); + std::vector<std::string> unit_movement_states({"left", "right", "down", "up"}); + + // every factions sub data set + for (size_t faction_idx = 0; faction_idx < unit_factions.size(); faction_idx++) + { + std::string faction = unit_factions.at(faction_idx); + // Create entry for units for in a faction + unit_textures.push_back(std::vector<std::vector<std::pair<SDL_Texture*, int>>>()); + + // every unit sub data set + for (size_t unit_idx = 0; unit_idx < units.size(); unit_idx++) + { + std::string unit = units.at(unit_idx); + + // Create entry for states for a unit + unit_textures.at(faction_idx).push_back(std::vector<std::pair<SDL_Texture*, int>>()); + + // every state sub data set + for (size_t state_idx = 0; state_idx < unit_states.size(); state_idx++) + { + std::string unit_state = unit_states.at(state_idx); + + HighFive::DataSet units_ds = + file.getDataSet("units/" + faction + "/" + unit + "/" + unit_state); + + std::vector<std::vector<std::vector<uint32_t>>> unit_frames; + units_ds.read(unit_frames); + + std::vector<uint32_t> unit_buffer(16 * 16 * unit_frames.size(), 0); + + for (size_t n = 0; n < unit_frames.size(); n++) + { + for (size_t y = 0; y < 16; y++) + { + for (size_t x = 0; x < 16; x++) + { + size_t index = (y * unit_frames.size() * 16) + (n * 16 + x); + + unit_buffer.at(index) = unit_frames.at(n).at(16 - y - 1).at(x); + } + } + } + + SDL_Texture* tmp = SDL_CreateTexture( + engine.renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, + unit_frames.size() * 16, 16); + + SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND); + + if (tmp == nullptr) + { + throw std::runtime_error( + "Fehler beim Erstellen der Textur für die Units: " + + std::string(SDL_GetError())); + } + + if (SDL_UpdateTexture( + tmp, NULL, unit_buffer.data(), unit_frames.size() * 16 * sizeof(int32_t)) != + 0) + { + throw std::runtime_error( + "Fehler beim updaten der Textur für die Units: " + + std::string(SDL_GetError())); + } + + unit_textures.at(faction_idx) + .at(unit_idx) + .push_back(std::pair<SDL_Texture*, int>(tmp, unit_frames.size())); + } - for (size_t n = 0; n < unit_frames.size(); n++) { - for (size_t y = 0; y < 24; y++) { - for (size_t x = 0; x < 24; x++) { - size_t index = (y * unit_frames.size() * 24) + (n * 24 + x); + // every movement state sub data set + for (size_t movement_state_idx = 0; movement_state_idx < unit_movement_states.size(); + movement_state_idx++) + { + std::string movement_state = unit_movement_states.at(movement_state_idx); + + HighFive::DataSet units_ds = file.getDataSet( + "units/" + faction + "/" + unit + "/movement/" + movement_state); + + std::vector<std::vector<std::vector<uint32_t>>> unit_frames; + units_ds.read(unit_frames); + + std::vector<uint32_t> unit_buffer(24 * 24 * unit_frames.size(), 0); + + for (size_t n = 0; n < unit_frames.size(); n++) + { + for (size_t y = 0; y < 24; y++) + { + for (size_t x = 0; x < 24; x++) + { + size_t index = (y * unit_frames.size() * 24) + (n * 24 + x); + + unit_buffer.at(index) = unit_frames.at(n).at(24 - y - 1).at(x); + } + } + } + + SDL_Texture* tmp = SDL_CreateTexture( + engine.renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, + unit_frames.size() * 24, 24); + + SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND); + + if (tmp == nullptr) + { + throw std::runtime_error( + "Fehler beim Erstellen der Textur für die Units: " + + std::string(SDL_GetError())); + } + + if (SDL_UpdateTexture( + tmp, NULL, unit_buffer.data(), unit_frames.size() * 24 * sizeof(int32_t)) != + 0) + { + throw std::runtime_error( + "Fehler beim updaten der Textur für die Units: " + + std::string(SDL_GetError())); + } + + unit_textures.at(faction_idx) + .at(unit_idx) + .push_back(std::pair<SDL_Texture*, int>(tmp, unit_frames.size())); + } + } + } - unit_buffer.at(index) = unit_frames.at(n).at(24 - y - 1).at(x); + this->unit_width = 16; + this->unit_height = 16; + this->unit_moving_width = 24; + this->unit_moving_height = 24; + + // Effects + std::vector<std::string> effects( + {"land_explosion", "air_explosion", "naval_explosion", "submarine_hide", + "submarine_appear"}); + + // Every effect sub data set + for (size_t effect_idx = 0; effect_idx < effects.size(); effect_idx++) + { + HighFive::DataSet effect_ds = file.getDataSet("effects/" + effects[effect_idx]); + + std::vector<std::vector<std::vector<uint32_t>>> effect_frames; + effect_ds.read(effect_frames); + + std::vector<uint32_t> effect_buffer(32 * 32 * effect_frames.size(), 0); + + // every animation frame + for (size_t n = 0; n < effect_frames.size(); n++) + { + for (size_t y = 0; y < 32; y++) + { + for (size_t x = 0; x < 32; x++) + { + size_t index = (y * effect_frames.size() * 32) + (n * 32 + x); + + effect_buffer.at(index) = effect_frames.at(n).at(32 - y - 1).at(x); + } } - } } - SDL_Texture *tmp = SDL_CreateTexture( - engine.renderer(), SDL_PIXELFORMAT_RGBA8888, - SDL_TEXTUREACCESS_STATIC, unit_frames.size() * 24, 24); + SDL_Texture* tmp = SDL_CreateTexture( + engine.renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, + effect_frames.size() * 32, 32); SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND); - if (tmp == nullptr) { - throw std::runtime_error( - "Fehler beim Erstellen der Textur für die Units: " + - std::string(SDL_GetError())); + if (tmp == nullptr) + { + throw std::runtime_error( + "Fehler beim Erstellen der Textur für die Effects: " + std::string(SDL_GetError())); } - if (SDL_UpdateTexture(tmp, NULL, unit_buffer.data(), - unit_frames.size() * 24 * sizeof(int32_t)) != 0) { - throw std::runtime_error( - "Fehler beim updaten der Textur für die Units: " + - std::string(SDL_GetError())); + if (SDL_UpdateTexture( + tmp, NULL, effect_buffer.data(), effect_frames.size() * 32 * sizeof(int32_t)) != 0) + { + throw std::runtime_error( + "Fehler beim updaten der Textur für die Tiles: " + std::string(SDL_GetError())); } - unit_textures.at(faction_idx) - .at(unit_idx) - .push_back(std::pair<SDL_Texture *, int>(tmp, unit_frames.size())); - } + effect_textures.push_back(std::pair<SDL_Texture*, int>(tmp, effect_frames.size())); } - } - - this->unit_width = 16; - this->unit_height = 16; - this->unit_moving_width = 24; - this->unit_moving_height = 24; - - // Effects - std::vector<std::string> effects({"land_explosion", "air_explosion", - "naval_explosion", "submarine_hide", - "submarine_appear"}); - - for (size_t effect_idx = 0; effect_idx < effects.size(); effect_idx++) { - HighFive::DataSet effect_ds = - file.getDataSet("effects/" + effects[effect_idx]); - - std::vector<std::vector<std::vector<uint32_t>>> effect_frames; - effect_ds.read(effect_frames); - - std::vector<uint32_t> effect_buffer(32 * 32 * effect_frames.size(), 0); - - for (size_t n = 0; n < effect_frames.size(); n++) { - for (size_t y = 0; y < 32; y++) { - for (size_t x = 0; x < 32; x++) { - size_t index = (y * effect_frames.size() * 32) + (n * 32 + x); - - effect_buffer.at(index) = effect_frames.at(n).at(32 - y - 1).at(x); - } - } - } - - SDL_Texture *tmp = SDL_CreateTexture( - engine.renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, - effect_frames.size() * 32, 32); - - SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND); - - if (tmp == nullptr) { - throw std::runtime_error( - "Fehler beim Erstellen der Textur für die Effects: " + - std::string(SDL_GetError())); - } - - if (SDL_UpdateTexture(tmp, NULL, effect_buffer.data(), - effect_frames.size() * 32 * sizeof(int32_t)) != 0) { - throw std::runtime_error( - "Fehler beim updaten der Textur für die Tiles: " + - std::string(SDL_GetError())); - } - - effect_textures.push_back( - std::pair<SDL_Texture *, int>(tmp, effect_frames.size())); - } - this->effect_width = 32; - this->effect_height = 32; + this->effect_width = 32; + this->effect_height = 32; } // Tiles -int Spritesheet::get_tile_width() { return tile_width; } +int Spritesheet::get_tile_width() +{ + return tile_width; +} -int Spritesheet::get_tile_height() { return tile_height; } +int Spritesheet::get_tile_height() +{ + return tile_height; +} -std::vector<std::pair<SDL_Texture *, int>> &Spritesheet::get_tile_textures() { - return tile_textures; +std::vector<std::pair<SDL_Texture*, int>>& Spritesheet::get_tile_textures() +{ + return tile_textures; } // Buildings -int Spritesheet::get_building_width() { return this->building_width; } +int Spritesheet::get_building_width() +{ + return this->building_width; +} -int Spritesheet::get_building_height() { return this->building_height; } +int Spritesheet::get_building_height() +{ + return this->building_height; +} -std::vector<SDL_Texture *> &Spritesheet::get_building_textures() { - return building_textures; +std::vector<SDL_Texture*>& Spritesheet::get_building_textures() +{ + return building_textures; } // Units -int Spritesheet::get_unit_width() { return this->unit_width; } +int Spritesheet::get_unit_width() +{ + return this->unit_width; +} -int Spritesheet::get_unit_height() { return this->unit_height; } +int Spritesheet::get_unit_height() +{ + return this->unit_height; +} -int Spritesheet::get_unit_moving_width() { return this->unit_moving_width; } +int Spritesheet::get_unit_moving_width() +{ + return this->unit_moving_width; +} -int Spritesheet::get_unit_moving_height() { return this->unit_moving_height; } +int Spritesheet::get_unit_moving_height() +{ + return this->unit_moving_height; +} -std::vector<std::vector<std::vector<std::pair<SDL_Texture *, int>>>> & -Spritesheet::get_unit_textures() { - return this->unit_textures; +std::vector<std::vector<std::vector<std::pair<SDL_Texture*, int>>>>& +Spritesheet::get_unit_textures() +{ + return this->unit_textures; } // Effects -int Spritesheet::get_effect_width() { return this->effect_width; } +int Spritesheet::get_effect_width() +{ + return this->effect_width; +} -int Spritesheet::get_effect_height() { return this->effect_height; } +int Spritesheet::get_effect_height() +{ + return this->effect_height; +} -std::vector<std::pair<SDL_Texture *, int>> &Spritesheet::get_effect_textures() { - return this->effect_textures; +std::vector<std::pair<SDL_Texture*, int>>& Spritesheet::get_effect_textures() +{ + return this->effect_textures; } -Spritesheet::~Spritesheet() { - for (std::pair<SDL_Texture *, int> tile_texture : tile_textures) { - SDL_DestroyTexture(tile_texture.first); - } - - for (SDL_Texture *building_texture : building_textures) { - SDL_DestroyTexture(building_texture); - } - - for (std::vector<std::vector<std::pair<SDL_Texture *, int>>> faction : - unit_textures) { - for (std::vector<std::pair<SDL_Texture *, int>> unit : faction) { - for (std::pair<SDL_Texture *, int> state : unit) { - SDL_DestroyTexture(state.first); - } +Spritesheet::~Spritesheet() +{ + for (std::pair<SDL_Texture*, int> tile_texture : tile_textures) + { + SDL_DestroyTexture(tile_texture.first); + } + + for (SDL_Texture* building_texture : building_textures) + { + SDL_DestroyTexture(building_texture); + } + + for (std::vector<std::vector<std::pair<SDL_Texture*, int>>> faction : unit_textures) + { + for (std::vector<std::pair<SDL_Texture*, int>> unit : faction) + { + for (std::pair<SDL_Texture*, int> state : unit) + { + SDL_DestroyTexture(state.first); + } + } } - } } } // namespace advanced_wars \ No newline at end of file diff --git a/src/spritesheet.hpp b/src/spritesheet.hpp index d7d8b07..c6ae316 100644 --- a/src/spritesheet.hpp +++ b/src/spritesheet.hpp @@ -1,81 +1,180 @@ +/** + * Spritesheet.hpp + * + * @date 30.1.2025 + * @author Frederik Keens + * @author David Maul + */ + #pragma once +#include <SDL_render.h> #include <SDL.h> #include <SDL_render.h> #include <string> #include <vector> -namespace advanced_wars { +namespace advanced_wars +{ // Forward declaration class Engine; -class Spritesheet { -public: - Spritesheet(std::string path, Engine &engine); - - ~Spritesheet(); - - Spritesheet(const Spritesheet &) = delete; - - Spritesheet &operator=(const Spritesheet &) = delete; - - // Tiles - - int get_tile_width(); - - int get_tile_height(); - - std::vector<std::pair<SDL_Texture *, int>> &get_tile_textures(); - - // Buildings - int get_building_width(); - - int get_building_height(); - - std::vector<SDL_Texture *> &get_building_textures(); - - // Units - int get_unit_width(); - - int get_unit_height(); - - int get_unit_moving_width(); - - int get_unit_moving_height(); - - std::vector<std::vector<std::vector<std::pair<SDL_Texture *, int>>>> & - get_unit_textures(); - - // Effects - int get_effect_width(); - - int get_effect_height(); - - std::vector<std::pair<SDL_Texture *, int>> &get_effect_textures(); - -private: - // Tiles - int tile_width; - int tile_height; - std::vector<std::pair<SDL_Texture *, int>> tile_textures; - - // Buildings - std::vector<SDL_Texture *> building_textures; - int building_width; - int building_height; - - // Units - std::vector<std::vector<std::vector<std::pair<SDL_Texture *, int>>>> - unit_textures; - int unit_width; - int unit_height; - int unit_moving_width; - int unit_moving_height; - - // Effects - std::vector<std::pair<SDL_Texture *, int>> effect_textures; - int effect_width; - int effect_height; +/** + * Spritesheet representation + */ +class Spritesheet +{ + public: + /** + * Constructor + * + * @param path Path to the file to load the spritesheet from + * @param path Engine object with valid SDL context + */ + Spritesheet(std::string path, Engine& engine); + + /** + * Destructor + */ + ~Spritesheet(); + + Spritesheet(const Spritesheet&) = delete; + + Spritesheet& operator=(const Spritesheet&) = delete; + + // Tiles + + /** + * @return The width of a floor tile in pixels + */ + int get_tile_width(); + + /** + * @return The height of a floor tile in pixels + */ + int get_tile_height(); + + /** + * Gets vector containing the pairs of an SDL Texture + * and the number animations steps it has. + * + * E.g. The vector at 1 contains a pair of an SDL Texture for water, its animations + * and the number of animation steps. Animation frames are store linearised. + * + * Which index respresents which tile can be found in the enum TileId of tile.hpp + * + * @return A vector of all floor tile textures and their animations + */ + std::vector<std::pair<SDL_Texture*, int>>& get_tile_textures(); + + // Buildings + + /** + * @return The width of a building in pixels + */ + int get_building_width(); + + /** + * @return The height of a building in pixels + */ + int get_building_height(); + + /** + * Every element represents the texture for all buildings from a faction linearised. + * + * Which FactionId represents which color + * can be found in the BuildingId enum in building.hpp + * + * Order of the building sprites is the same as in buildingId enum in building.hpp + * + * @return Vector of all Building textures + */ + std::vector<SDL_Texture*>& get_building_textures(); + + // Units + + /** + * @return The width of a unit while standing still in pixels + */ + int get_unit_width(); + + /** + * @return The height of a unit while standing still in pixels + */ + int get_unit_height(); + + /** + * @return The width of a unit while moving in pixels + */ + int get_unit_moving_width(); + + /** + * @return The height of a unit while moving in pixels + */ + int get_unit_moving_height(); + + /** + * Gets the hierarchical vector of all unit textures. + * + * The vector groups the faction, the UnitId and the state for each unit. + * The pair consist of the texture for that combination and the number of animation steps. + * + * E.g. A red faction(0) Recon(2) Unit that's unavailable(1) would be at [0][2][1]. + * Animation frames are linearised. + * + * Indices can be found in the enums of unit.hpp + * + * @return A 3-dimensional vector of pairs consisting of a texture and + * the number of animation steps + */ + std::vector<std::vector<std::vector<std::pair<SDL_Texture*, int>>>>& get_unit_textures(); + + // Effects + + /** + * @return The width of an effect in pixels + */ + int get_effect_width(); + + /** + * @return The height of an effect in pixels + */ + int get_effect_height(); + + /** + * Vector that contains pairs of effect textures and the number of animation steps it has. + * + * Each texture of an element is a pair of an effect + * and all its animation frames linearised. + * + * Which index represents which effect can be found in the EffectId enum in effect.hpp + * + * @return A vector of all effects and its animations + */ + std::vector<std::pair<SDL_Texture*, int>>& get_effect_textures(); + + private: + // Tiles + int tile_width; + int tile_height; + std::vector<std::pair<SDL_Texture*, int>> tile_textures; + + // Buildings + std::vector<SDL_Texture*> building_textures; + int building_width; + int building_height; + + // Units + std::vector<std::vector<std::vector<std::pair<SDL_Texture*, int>>>> unit_textures; + int unit_width; + int unit_height; + int unit_moving_width; + int unit_moving_height; + + // Effects + std::vector<std::pair<SDL_Texture*, int>> effect_textures; + int effect_width; + int effect_height; }; } // namespace advanced_wars diff --git a/src/tile.cpp b/src/tile.cpp index 909b7fc..525ee83 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -2,35 +2,33 @@ #include "spritesheet.hpp" #include <vector> -namespace advanced_wars { +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) +{ + Spritesheet* spritesheet = engine->get_spritesheet(); -void Tile::render(Engine &engine, int scale) { - Spritesheet *spritesheet = engine.get_spritesheet(); + int step = + engine->get_stage() % spritesheet->get_tile_textures().at(static_cast<int>(id)).second; - int step = engine.get_stage() % - spritesheet->get_tile_textures().at(static_cast<int>(id)).second; + SDL_Rect src; + src.x = step * spritesheet->get_tile_width(); + src.y = 0; + src.w = spritesheet->get_tile_width(); + src.h = spritesheet->get_tile_height(); - SDL_Rect src; - src.x = step * spritesheet->get_tile_width(); - src.y = 0; - src.w = spritesheet->get_tile_width(); - src.h = spritesheet->get_tile_height(); + SDL_Rect dest; + dest.x = x * spritesheet->get_tile_width() * scale; + dest.y = y * spritesheet->get_tile_height() * scale; + dest.w = spritesheet->get_tile_width() * scale; + dest.h = spritesheet->get_tile_height() * scale; - SDL_Rect dest; - dest.x = x * spritesheet->get_tile_width() * scale; - dest.y = y * spritesheet->get_tile_height() * scale; - dest.w = spritesheet->get_tile_width() * scale; - dest.h = spritesheet->get_tile_height() * scale; - - SDL_RenderCopyEx( - engine.renderer(), - spritesheet->get_tile_textures().at(static_cast<int>(id)).first, &src, - &dest, 0, NULL, SDL_FLIP_NONE); + SDL_RenderCopyEx( + engine->renderer(), spritesheet->get_tile_textures().at(static_cast<int>(id)).first, &src, + &dest, 0, NULL, SDL_FLIP_NONE); } } // namespace advanced_wars \ No newline at end of file diff --git a/src/tile.hpp b/src/tile.hpp index cf54b32..ae147b8 100644 --- a/src/tile.hpp +++ b/src/tile.hpp @@ -3,49 +3,52 @@ #include "engine.hpp" #include "scene.hpp" -namespace advanced_wars { +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, +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: - Tile(TileId id, int x, int y); - TileId id; - int x; - int y; +class Tile +{ + public: + Tile(TileId id, int x, int y); + TileId id; + int x; + int y; - void render(Engine &engine, int scale); + void render(Engine* engine, int scale); }; } // namespace advanced_wars \ No newline at end of file diff --git a/src/ui/contextmenu.cpp b/src/ui/contextmenu.cpp new file mode 100644 index 0000000..8bae5ff --- /dev/null +++ b/src/ui/contextmenu.cpp @@ -0,0 +1,83 @@ +#include "contextmenu.hpp" +#include <iostream> +#include <SDL_ttf.h> + +namespace advanced_wars { + + ContextMenu::ContextMenu() + : selectedOption(0) {} + + ContextMenu::~ContextMenu() {} + + void ContextMenu::setOptions(const std::vector<std::string>& newOptions) { + options = newOptions; + selectedOption = 0; // Reset auf die erste Option + } + + void ContextMenu::render(Engine* engine) { + if (!options.empty()) { + if (TTF_Init() == -1) { + std::cerr << "Failed to initialize TTF: " << TTF_GetError() << std::endl; + return; + } + + std::string basePath = SDL_GetBasePath(); + std::string relativePath = "assets/ARCADECLASSIC.TTF"; + std::string fullPath = basePath + relativePath; + TTF_Font *font = TTF_OpenFont(fullPath.c_str(), 16); + if (!font) { + std::cerr << "Failed to load font: " << TTF_GetError() << std::endl; + return; + } + + SDL_Color white = {255, 255, 255, 255}; + SDL_Color yellow = {192, 255, 0, 255}; + + int spacing = 20; // Abstand zwischen den Optionen + + //box around options + SDL_SetRenderDrawColor(engine->renderer(), 0, 0, 255, 255); + SDL_Rect box = {x, y - 3, 50, static_cast<int>(options.size() * spacing)}; + SDL_RenderFillRect(engine->renderer(), &box); + + SDL_SetRenderDrawColor(engine->renderer(), 0, 0, 0, 255); + + for (size_t i = 0; i < options.size(); ++i) { + SDL_Surface* textSurface = TTF_RenderText_Solid(font, options[i].c_str(), (i == selectedOption) ? yellow : white); + if (!textSurface) { + continue; + } + + SDL_Texture* textTexture = SDL_CreateTextureFromSurface(engine->renderer(), textSurface); + SDL_Rect textRect = {x+10, y + static_cast<int>(i * spacing), textSurface->w, textSurface->h}; + SDL_RenderCopy(engine->renderer(), textTexture, nullptr, &textRect); + + SDL_DestroyTexture(textTexture); + SDL_FreeSurface(textSurface); + } + + TTF_CloseFont(font); + TTF_Quit(); + } + } + + void ContextMenu::handleEvent(SDL_Event& event) { + if (event.type == SDL_KEYDOWN) { + if (event.key.keysym.sym == SDLK_DOWN) { + selectedOption = (selectedOption + 1) % options.size(); + } else if (event.key.keysym.sym == SDLK_UP) { + selectedOption = (selectedOption - 1 + options.size()) % options.size(); + } + } + } + + std::string ContextMenu::getSelectedOption() { + return options[selectedOption]; + } + + void ContextMenu::update(int x, int y) { + this->x = x; + this->y = y; + } + +} diff --git a/src/ui/contextmenu.hpp b/src/ui/contextmenu.hpp new file mode 100644 index 0000000..94b095f --- /dev/null +++ b/src/ui/contextmenu.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include <SDL.h> +#include <vector> +#include <string> +#include "../scene.hpp" +#include "../engine.hpp" + +namespace advanced_wars { + +class ContextMenu : public Scene { +private: + size_t selectedOption; + std::vector<std::string> options; + int x; + int y; + +public: + ContextMenu(); + + void setOptions(const std::vector<std::string>& newOptions); + + void render(Engine* engine) override; + + void handleEvent(SDL_Event& event); + + void update(int x, int y); + + std::string getSelectedOption(); + + ~ContextMenu(); +}; + +} \ No newline at end of file diff --git a/src/ui/menu.cpp b/src/ui/menu.cpp new file mode 100644 index 0000000..8029729 --- /dev/null +++ b/src/ui/menu.cpp @@ -0,0 +1,249 @@ +#include "menu.hpp" +#include "../building.hpp" +#include "../level.hpp" +#include "../spritesheet.hpp" +#include "../tile.hpp" +#include "../unit.hpp" +#include <SDL.h> +#include <SDL_image.h> +#include <SDL_ttf.h> +#include <iostream> +#include <string> + +namespace advanced_wars +{ + +Menu::Menu(int selectedOption) + : selectedOption(selectedOption), options({"Start Game", "Options", "Exit"}), + backgroundTexture(nullptr) +{ + if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG)) + { + std::cerr << "Failed to initialize SDL_image: " << IMG_GetError() << std::endl; + } +} + +Menu::~Menu() +{ + if (backgroundTexture) + { + SDL_DestroyTexture(backgroundTexture); + } + IMG_Quit(); +}; + +void Menu::render(Engine* engine) +{ + + // Iterate over all events + while (!engine->events().empty()) + { + SDL_Event event = engine->events().at(0); + engine->events().pop_front(); + handleEvent(engine, event); + } + + if (backgroundTexture) + { + SDL_RenderCopy(engine->renderer(), backgroundTexture, nullptr, nullptr); + } + else + { + SDL_SetRenderDrawColor(engine->renderer(), 0, 0, 0, 255); + SDL_RenderClear(engine->renderer()); + } + + if (TTF_Init() == -1) + { + std::cerr << "Failed to initialize TTF: " << TTF_GetError() << std::endl; + return; + } + + std::string basePath = SDL_GetBasePath(); + std::string relativePath = "assets/ARCADECLASSIC.TTF"; + std::string fullPath = basePath + relativePath; + TTF_Font* titleFont = TTF_OpenFont(fullPath.c_str(), 48); + if (!titleFont) + { + std::cerr << "Failed to load title font: " << fullPath << TTF_GetError() << std::endl; + return; + } + + TTF_Font* menuFont = TTF_OpenFont(fullPath.c_str(), 24); + if (!menuFont) + { + TTF_CloseFont(titleFont); + std::cerr << "Failed to load menu font: " << fullPath << TTF_GetError() << std::endl; + return; + } + + SDL_Color white = {255, 255, 255, 255}; + SDL_Color yellow = {255, 255, 0, 255}; + + SDL_Surface* titleSurface = TTF_RenderText_Solid(titleFont, "Advanced Wars", white); + if (titleSurface) + { + SDL_Texture* titleTexture = SDL_CreateTextureFromSurface(engine->renderer(), titleSurface); + SDL_Rect titleRect = { + static_cast<int>((800 - titleSurface->w) / 2), 50, titleSurface->w, titleSurface->h}; + SDL_RenderCopy(engine->renderer(), titleTexture, nullptr, &titleRect); + SDL_DestroyTexture(titleTexture); + SDL_FreeSurface(titleSurface); + } + + for (size_t i = 0; i < options.size(); ++i) + { + SDL_Surface* textSurface = TTF_RenderText_Solid( + menuFont, options[i].c_str(), (i == selectedOption) ? yellow : white); + if (!textSurface) + { + continue; + } + + SDL_Texture* textTexture = SDL_CreateTextureFromSurface(engine->renderer(), textSurface); + SDL_Rect textRect = { + static_cast<int>((800 - textSurface->w) / 2), static_cast<int>(150 + i * 50), + textSurface->w, textSurface->h}; + SDL_RenderCopy(engine->renderer(), textTexture, nullptr, &textRect); + + SDL_DestroyTexture(textTexture); + SDL_FreeSurface(textSurface); + } + + TTF_CloseFont(titleFont); + TTF_CloseFont(menuFont); + TTF_Quit(); + + SDL_RenderPresent(engine->renderer()); +} + +void Menu::handleEvent(Engine* engine, SDL_Event& event) +{ + if (event.type == SDL_KEYDOWN) + { + if (event.key.keysym.sym == SDLK_DOWN) + { + selectedOption = (selectedOption + 1) % options.size(); + } + else if (event.key.keysym.sym == SDLK_UP) + { + selectedOption = (selectedOption - 1 + options.size()) % options.size(); + } + else if (event.key.keysym.sym == SDLK_RETURN) + { + if (options[selectedOption] == "Exit") + { + std::cout << "Exiting game..." << std::endl; + engine->exit(); + } + else if (options[selectedOption] == "Start Game") + { + std::cout << "Starting game..." << std::endl; + + // Construct a level + std::vector<Tile> tiles; + for (int y = 0; y < 20; y++) + { + for (int x = 0; x < 20; x++) + { + tiles.push_back(Tile(TileId::PLAIN, x, y)); + } + } + + // Fill the edges with water + for (size_t n = 0; n < 20; n++) + { + // Vertical + tiles.at(n * 20) = Tile(TileId::WATER, 0, n); + tiles.at(n * 20 + 19) = Tile(TileId::WATER, 19, n); + // Horizontal + tiles.at(n) = Tile(TileId::WATER, n, 0); + tiles.at(19 * 20 + n) = Tile(TileId::WATER, n, 19); + } + + // Make the edges cliffs + for (size_t n = 1; n < 19; n++) + { + // Vertical + tiles.at(n * 20 + 1) = Tile(TileId::CLIFF_RIGHT, 1, n); + tiles.at(n * 20 + 18) = Tile(TileId::CLIFF_LEFT, 18, n); + + // Horizontal + tiles.at(20 + n) = Tile(TileId::CLIFF_BOTTOM, n, 1); + tiles.at(18 * 20 + n) = Tile(TileId::CLIFF_TOP, n, 18); + } + + // Fix the corners + tiles.at(20 + 1) = Tile(TileId::CLIFF_CORNER_TOP_LEFT, 1, 1); + tiles.at(20 + 18) = Tile(TileId::CLIFF_CORNER_TOP_RIGHT, 18, 1); + tiles.at(18 * 20 + 1) = Tile(TileId::CLIFF_CORNER_BOTTOM_LEFT, 1, 18); + tiles.at(18 * 20 + 18) = Tile(TileId::CLIFF_CORNER_BOTTOM_RIGHT, 18, 18); + + // Buildings + std::vector<Building> buildings; + + for (int y = 0; y < 6; y++) + { + for (int x = 0; x < 5; x++) + { + BuildingId id = static_cast<BuildingId>(x); + BuildingFaction faction = static_cast<BuildingFaction>(y); + + buildings.push_back(Building(3 + x, 3 + 2 * y, id, faction)); + } + } + + // Units + std::vector<Unit> units; + + for (int y = 0; y < 19; y++) + { + for (int x = 0; x < 6; x++) + { + units.push_back(Unit( + x + 9, y + 2, UnitFaction::URED, static_cast<UnitId>(y), + static_cast<UnitState>(x))); + } + } + + std::vector<Effect> effects( + {Effect(3, 15, EffectId::LAND_EXPLOSION, false), + Effect(5, 15, EffectId::AIR_EXPLOSION, true), + Effect(5, 18, EffectId::NAVAL_EXPLOSION, true)}); + + std::shared_ptr<Level> level = + std::make_shared<Level>("Osnabrück", 20, 20, tiles, buildings, units, effects); + + engine->push_scene(level); + } + else if (options[selectedOption] == "Options") + { + std::cout << "Opening options..." << std::endl; + } + } + } +} + +void Menu::loadBackground(SDL_Renderer* renderer, const std::string& imagePath) +{ + // Lade das Hintergrundbild + SDL_Surface* backgroundSurface = IMG_Load(imagePath.c_str()); + if (!backgroundSurface) + { + std::cerr << "Failed to load background image: " << IMG_GetError() << std::endl; + return; + } + + // Erstelle eine Textur aus der Oberfläche und speichere sie als + // Klassenmitglied + backgroundTexture = SDL_CreateTextureFromSurface(renderer, backgroundSurface); + SDL_FreeSurface(backgroundSurface); // Oberfläche freigeben, da sie nicht mehr + // benötigt wird + + if (!backgroundTexture) + { + std::cerr << "Failed to create background texture: " << SDL_GetError() << std::endl; + } +} + +} // namespace advanced_wars diff --git a/src/ui/menu.hpp b/src/ui/menu.hpp new file mode 100644 index 0000000..43f84d0 --- /dev/null +++ b/src/ui/menu.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include "../scene.hpp" +#include <SDL.h> +#include <array> +#include <iostream> +#include <string> +#include <vector> + +namespace advanced_wars { + +/** + * @class Menu + * @brief Represents the main menu of the game, allowing navigation between different options. + * + * This menu provides three selectable options: + * - "Start Game": Begins a new game session. + * - "Options": Opens the game settings. + * - "Exit": Closes the application. + */ +class Menu : public Scene { +private: + size_t selectedOption; ///< Index of the currently selected menu option. + std::array<std::string, 3> options; ///< The available menu options. + SDL_Texture *backgroundTexture; ///< Pointer to the background texture (if any). + +public: + /** + * @brief Constructs the Menu with an initial selected option. + * + * Initializes the menu with the available options and sets the currently + * selected option based on the given index. + * + * @param selectedOption The index of the initially selected menu option. + */ + Menu(int selectedOption); + + /** + * @brief Renders the menu on the screen. + * + * This method clears the screen, draws the background (if available), + * renders the menu title, and displays the selectable options. The currently + * selected option is highlighted in a different color. + * + * @param engine Pointer to the game engine, used for rendering. + */ + void render(Engine *engine) override; + + /** + * @brief Handles user input events for menu navigation. + * + * This method processes keyboard input to navigate through the menu options. + * - **Arrow Down (`SDLK_DOWN`)**: Moves the selection to the next option. + * - **Arrow Up (`SDLK_UP`)**: Moves the selection to the previous option. + * - **Enter (`SDLK_RETURN`)**: Confirms the selection: + * - **"Start Game"**: Loads the game scene. + * - **"Options"**: Opens the settings menu. + * - **"Exit"**: Closes the application. + * + * @param engine Pointer to the game engine, used to manage scenes. + * @param event The SDL event containing user input data. + */ + void handleEvent(Engine *engine, SDL_Event &event); + + /** + * @brief Loads a background image as a texture. + * + * This method loads an image file, converts it into an SDL texture, and + * assigns it as the menu's background. If the loading fails, an error is + * logged, and the menu will display a plain black background instead. + * + * @param renderer The SDL renderer used to create the texture. + * @param imagePath The file path to the background image. + */ + void loadBackground(SDL_Renderer *renderer, const std::string &imagePath); + + /** + * @brief Destroys the menu and releases resources. + * + * Cleans up allocated resources, including the background texture (if loaded), + * and ensures that SDL_Image is properly shut down. + */ + ~Menu(); +}; + +} // namespace advanced_wars \ No newline at end of file diff --git a/src/ui/pausemenu.cpp b/src/ui/pausemenu.cpp new file mode 100644 index 0000000..0dd5928 --- /dev/null +++ b/src/ui/pausemenu.cpp @@ -0,0 +1,118 @@ +#include "pausemenu.hpp" +#include "../engine.hpp" +#include <SDL_ttf.h> + +namespace advanced_wars { + +PauseMenu::PauseMenu(int selectedOption, SDL_Texture *backgroundTexture) + : selectedOption(selectedOption), + options({"Resume", "Options", "Exit"}), backgroundTexture(backgroundTexture) { + // Initialize SDL_ttf + if (TTF_Init() == -1) { + std::cerr << "Failed to initialize SDL_ttf: " << TTF_GetError() << std::endl; + } + + if (!backgroundTexture) { + this->backgroundTexture = nullptr; + } +} + +PauseMenu::~PauseMenu() { + if (backgroundTexture) { + SDL_DestroyTexture(backgroundTexture); + backgroundTexture = nullptr; + } + TTF_Quit(); +} + +void PauseMenu::render(Engine *engine) { + + while (!engine->events().empty()) { + SDL_Event event = engine->events().at(0); + engine->events().pop_front(); + handleEvent(engine, event); + } + + SDL_Renderer *renderer = engine->renderer(); + + // Render the existing level + //engine->render(); + + // Render the dialog background + if (backgroundTexture) { + SDL_RenderCopy(renderer, backgroundTexture, nullptr, nullptr); + } + + if (TTF_Init() == -1) { + std::cerr << "Failed to initialize TTF: " << TTF_GetError() << std::endl; + return; + } + + // Render the dialog options on top of the background + std::string basePath = SDL_GetBasePath(); + std::string relativePath = "assets/ARCADECLASSIC.TTF"; + std::string fullPath = basePath + relativePath; + + TTF_Font *font = TTF_OpenFont(fullPath.c_str(), 24); + if (!font) { + std::cerr << "Failed to load menu font: " << fullPath << " " << TTF_GetError() + << std::endl; + return; + } + + SDL_Color white = {255, 255, 255, 255}; + SDL_Color yellow = {255, 255, 0, 255}; + + for (size_t i = 0; i < options.size(); ++i) { + SDL_Surface *textSurface = TTF_RenderText_Solid( + font, options[i].c_str(), (i == selectedOption) ? yellow : white); + SDL_Texture *textTexture = SDL_CreateTextureFromSurface(renderer, textSurface); + + SDL_Rect destRect = {100, static_cast<int>(100 + i * 50), textSurface->w, textSurface->h}; + SDL_RenderCopy(renderer, textTexture, nullptr, &destRect); + + SDL_FreeSurface(textSurface); + SDL_DestroyTexture(textTexture); + } + TTF_CloseFont(font); + SDL_RenderPresent(renderer); +} + +void PauseMenu::handleEvent(Engine *engine, SDL_Event &event) { + if (event.type == SDL_KEYDOWN) { + if (event.key.keysym.sym == SDLK_DOWN) { + selectedOption = (selectedOption + 1) % options.size(); + } else if (event.key.keysym.sym == SDLK_UP) { + selectedOption = (selectedOption - 1 + options.size()) % options.size(); + } else if (event.key.keysym.sym == SDLK_ESCAPE) { + std::cout << "Resuming game..." << std::endl; + engine->pop_scene(); + } else if (event.key.keysym.sym == SDLK_RETURN) { + if (options[selectedOption] == "Exit") { + // exit into main menu + std::cout << "Exiting game..." << std::endl; + engine->return_to_menu(); + } else if (options[selectedOption] == "Resume") { + // resume game + std::cout << "Resuming game..." << std::endl; + engine->pop_scene(); + } + } + + } + // Handle events for the pause menu +} + + + +void PauseMenu::loadBackground(SDL_Renderer *renderer, const std::string &imagePath) { + SDL_Surface *surface = IMG_Load(imagePath.c_str()); + if (!surface) { + std::cerr << "Failed to load image: " << IMG_GetError() << std::endl; + return; + } + backgroundTexture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_FreeSurface(surface); +} + +} // namespace advanced_wars \ No newline at end of file diff --git a/src/ui/pausemenu.hpp b/src/ui/pausemenu.hpp new file mode 100644 index 0000000..6dc2d2c --- /dev/null +++ b/src/ui/pausemenu.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include "../scene.hpp" +#include <SDL.h> +#include <array> +#include <iostream> +#include <string> +#include <vector> +#include <SDL_image.h> +#include <SDL_ttf.h> + +namespace advanced_wars { + +/** + * @class PauseMenu + * @brief A scene that represents the in-game pause menu. + * + * The pause menu allows the player to: + * - **Resume**: Return to the current game scene. + * - **Options**: (Currently not implemented). + * - **Exit**: Return to the main menu. + * + * The menu supports keyboard navigation and responds to user input. + */ +class PauseMenu : public Scene { +private: + size_t selectedOption; ///< Index of the currently selected menu option. + std::array<std::string, 3> options; ///< The available pause menu options. + SDL_Texture *backgroundTexture; ///< Pointer to the background texture (if available). + +public: + /** + * @brief Constructs the pause menu with a background texture. + * + * The pause menu initializes the menu options and stores the provided + * background texture. If no texture is provided, a default black background is used. + * + * @param selectedOption The index of the initially selected menu option. + * @param backgroundTexture A pointer to the background texture (can be nullptr). + */ + PauseMenu(int selectedOption, SDL_Texture *backgroundTexture); + + /** + * @brief Renders the pause menu on the screen. + * + * This method: + * - Draws the background (if available). + * - Displays the menu options with the currently selected option highlighted. + * - Presents the rendered frame to the screen. + * + * @param engine Pointer to the game engine, used for rendering. + */ + void render(Engine *engine) override; + + /** + * @brief Handles user input events for menu navigation. + * + * This method processes keyboard input to navigate and interact with the pause menu. + * - **Arrow Down (`SDLK_DOWN`)**: Moves the selection to the next option. + * - **Arrow Up (`SDLK_UP`)**: Moves the selection to the previous option. + * - **Escape (`SDLK_ESCAPE`)**: Resumes the game by removing the pause menu. + * - **Enter (`SDLK_RETURN`)**: Executes the selected option: + * - **"Resume"**: Closes the pause menu and resumes the game. + * - **"Exit"**: Returns to the main menu. + * + * @param engine Pointer to the game engine, used to manage scenes. + * @param event The SDL event containing user input data. + */ + void handleEvent(Engine *engine, SDL_Event &event); + + /** + * @brief Loads a background image as a texture. + * + * This method loads an image file, converts it into an SDL texture, and assigns it + * as the menu’s background. If the loading fails, an error is logged, and the menu + * will display a plain black background instead. + * + * @param renderer The SDL renderer used to create the texture. + * @param imagePath The file path to the background image. + */ + void loadBackground(SDL_Renderer *renderer, const std::string &imagePath); + + /** + * @brief Destroys the pause menu and releases resources. + * + * Cleans up allocated resources, including the background texture (if loaded), + * and ensures that SDL_ttf is properly shut down. + */ + ~PauseMenu(); +}; + +} // namespace advanced_wars \ No newline at end of file diff --git a/src/unit.cpp b/src/unit.cpp index c938f48..4ece9fb 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -1,187 +1,219 @@ #include "unit.hpp" #include <iostream> -namespace advanced_wars { +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), 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) { - Spritesheet *spritesheet = engine.get_spritesheet(); - - int step = engine.get_stage() % spritesheet->get_unit_textures() - .at(static_cast<int>(faction)) - .at(static_cast<int>(id)) - .at(static_cast<int>(state)) - .second; - - if (state == UnitState::IDLE || state == UnitState::UNAVAILABLE) { - - SDL_Rect src; - src.x = step * spritesheet->get_unit_width(); - src.y = 0; - src.w = spritesheet->get_unit_width(); - src.h = spritesheet->get_unit_height(); - - SDL_Rect dst; - dst.x = x * spritesheet->get_unit_width() * scale; - dst.y = y * spritesheet->get_unit_height() * scale; - dst.w = spritesheet->get_unit_width() * scale; - dst.h = spritesheet->get_unit_height() * scale; - - SDL_RenderCopyEx(engine.renderer(), - spritesheet->get_unit_textures() - .at(static_cast<int>(faction)) - .at(static_cast<int>(id)) - .at(static_cast<int>(state)) - .first, - &src, &dst, 0, NULL, SDL_FLIP_NONE); - } else { - // The moving states have a resolution of 24x24 instead of 16x16 and need to - // be handled separately - SDL_Rect src; - src.x = step * spritesheet->get_unit_moving_width(); - src.y = 0; - src.w = spritesheet->get_unit_moving_width(); - src.h = spritesheet->get_unit_moving_height(); - - SDL_Rect dst; - dst.x = ((x * spritesheet->get_unit_width()) - 4) * scale; - dst.y = ((y * spritesheet->get_unit_height()) - 4) * scale; - dst.w = spritesheet->get_unit_moving_width() * scale; - dst.h = spritesheet->get_unit_moving_height() * scale; - - SDL_RenderCopyEx(engine.renderer(), - spritesheet->get_unit_textures() - .at(static_cast<int>(faction)) - .at(static_cast<int>(id)) - .at(static_cast<int>(state)) - .first, - &src, &dst, 0, NULL, SDL_FLIP_NONE); - } + : 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) +{ + Spritesheet* spritesheet = engine->get_spritesheet(); + + int step = engine->get_stage() % spritesheet->get_unit_textures() + .at(static_cast<int>(faction)) + .at(static_cast<int>(id)) + .at(static_cast<int>(state)) + .second; + + if (state == UnitState::IDLE || state == UnitState::UNAVAILABLE) + { + + SDL_Rect src; + src.x = step * spritesheet->get_unit_width(); + src.y = 0; + src.w = spritesheet->get_unit_width(); + src.h = spritesheet->get_unit_height(); + + SDL_Rect dst; + dst.x = x * spritesheet->get_unit_width() * scale; + dst.y = y * spritesheet->get_unit_height() * scale; + dst.w = spritesheet->get_unit_width() * scale; + dst.h = spritesheet->get_unit_height() * scale; + + SDL_RenderCopyEx( + engine->renderer(), + spritesheet->get_unit_textures() + .at(static_cast<int>(faction)) + .at(static_cast<int>(id)) + .at(static_cast<int>(state)) + .first, + &src, &dst, 0, NULL, SDL_FLIP_NONE); + } + else + { + // The moving states have a resolution of 24x24 instead of 16x16 and need to + // be handled separately + SDL_Rect src; + src.x = step * spritesheet->get_unit_moving_width(); + src.y = 0; + src.w = spritesheet->get_unit_moving_width(); + src.h = spritesheet->get_unit_moving_height(); + + SDL_Rect dst; + dst.x = ((x * spritesheet->get_unit_width()) - 4) * scale; + dst.y = ((y * spritesheet->get_unit_height()) - 4) * scale; + dst.w = spritesheet->get_unit_moving_width() * scale; + dst.h = spritesheet->get_unit_moving_height() * scale; + + SDL_RenderCopyEx( + engine->renderer(), + spritesheet->get_unit_textures() + .at(static_cast<int>(faction)) + .at(static_cast<int>(id)) + .at(static_cast<int>(state)) + .first, + &src, &dst, 0, NULL, SDL_FLIP_NONE); + } } -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); +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; - 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; + } - // 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 (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; } - } - - 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; + 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; + } } - } - - // 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); +void Unit::update_position(int posX, int posY) +{ + calc_state(posX, posY); - this->x = posX; - this->y = 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; +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 (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; + 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; + 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::vector<Unit> &unitVector) { +void Unit::on_left_click(SDL_Event event) +{ - std::cout << "Left-button pressed on unit: " << this->health << std::endl; + 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; +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 722e52f..489dd3c 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -1,137 +1,139 @@ #pragma once -#include <unordered_map> #include "engine.hpp" #include "weapon.hpp" #include <optional> +#include <unordered_map> -namespace advanced_wars { +namespace advanced_wars +{ -enum class UnitFaction { - URED = 0, - UBLUE = 1, - UGREEN = 2, - UYELLOW = 3, - UPURPLE = 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, +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, }; -enum class UnitState { - IDLE = 0, - UNAVAILABLE = 1, - MOVEMENTLEFT = 2, - MOVEMENTRIGHT = 3, - MOVEMENTDOWN = 4, - MOVEMENTUP = 5, +enum class UnitState +{ + IDLE = 0, + UNAVAILABLE = 1, + MOVEMENTLEFT = 2, + MOVEMENTRIGHT = 3, + MOVEMENTDOWN = 4, + MOVEMENTUP = 5, }; -enum class MovementType { - FOOT = 0, - TIRES = 1, - TREAD = 2, - AIR = 3, - SHIP = 4, - LANDER = 5, +enum class MovementType +{ + FOOT = 0, + TIRES = 1, + TREAD = 2, + AIR = 3, + SHIP = 4, + 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, std::vector<Unit> &unitVector); - -private: - 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; - +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: + 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 index b93d09c..e324130 100644 --- a/src/weapon.cpp +++ b/src/weapon.cpp @@ -2,26 +2,28 @@ namespace advanced_wars { - Weapon::Weapon() : name(""), damage() {} +Weapon::Weapon() : name(""), damage() {} - Weapon::Weapon(const std::string &weaponName, const std::unordered_map<UnitId, int> &damageValues) - : name(weaponName), damage(damageValues) {} +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 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 +// Funktion zum Abrufen eines Schadenswertes +int Weapon::getDamageValue(UnitId unitId) const +{ + auto it = damage.find(unitId); + if (it != damage.end()) { - auto it = damage.find(unitId); - if (it != damage.end()) - { - return it->second; - } - return 0; // oder ein Fehlerwert + return it->second; } + return 0; // oder ein Fehlerwert +} -} \ No newline at end of file +} // namespace advanced_wars \ No newline at end of file diff --git a/src/weapon.hpp b/src/weapon.hpp index 2192e61..028eef1 100644 --- a/src/weapon.hpp +++ b/src/weapon.hpp @@ -3,27 +3,29 @@ #include <string> #include <unordered_map> -namespace advanced_wars { +namespace advanced_wars +{ enum class UnitId; -class Weapon { -public: - // Konstruktoren - Weapon(); - Weapon(const std::string &weaponName, const std::unordered_map<UnitId, int> &damageValues); +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 hinzuzufügen + void addDamageValue(UnitId unitId, int value); - // Methode, um einen Schadenswert abzurufen - int getDamageValue(UnitId unitId) const; + // Methode, um einen Schadenswert abzurufen + int getDamageValue(UnitId unitId) const; - // Name der Waffe - std::string name; + // Name der Waffe + std::string name; - // Schadenstabelle - std::unordered_map<UnitId, int> damage; + // Schadenstabelle + std::unordered_map<UnitId, int> damage; }; } // namespace advanced_wars diff --git a/src/window.cpp b/src/window.cpp index aa75681..926cbf7 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,35 +1,49 @@ #include "window.hpp" #include <stdexcept> -namespace advanced_wars { - -Window::Window(std::string title, int w, int h) { - /// Init width and height - width = w; - height = h; - - // Generate SDL main window - window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, width, height, - SDL_WINDOW_SHOWN); - - if (window == nullptr) { - throw std::runtime_error("SDL window could not be generated: " + - std::string(SDL_GetError())); - } +namespace advanced_wars +{ + +Window::Window(std::string title, int w, int h) +{ + /// Init width and height + width = w; + height = h; + + // Generate SDL main window + window = SDL_CreateWindow( + title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, + SDL_WINDOW_SHOWN); + + if (window == nullptr) + { + throw std::runtime_error( + "SDL window could not be generated: " + std::string(SDL_GetError())); + } } -int Window::w() { return width; } +int Window::w() +{ + return width; +} -int Window::h() { return height; } +int Window::h() +{ + return height; +} -SDL_Window *Window::sdl_window() { return window; } +SDL_Window* Window::sdl_window() +{ + return window; +} -Window::~Window() { - if (window) { - SDL_DestroyWindow(window); - window = nullptr; - } +Window::~Window() +{ + if (window) + { + SDL_DestroyWindow(window); + window = nullptr; + } } } // namespace advanced_wars diff --git a/src/window.hpp b/src/window.hpp index 9cd664d..a53586b 100644 --- a/src/window.hpp +++ b/src/window.hpp @@ -3,50 +3,52 @@ #include <SDL.h> #include <string> -namespace advanced_wars { +namespace advanced_wars +{ /** * @brief The main window of the game */ -class Window { -public: - /*** - * Creates a main window with given \ref title, width \ref w and height \ref h - * - * @param title Title of the window - * @param w Width - * @param h Height - */ - Window(std::string title, int w, int h); - - /** - * Forbids the creation of copies of a window - */ - Window(const Window &) = delete; - // Window& operator=(const Window&) = delete; - - /*** - * Destructor. - */ - ~Window(); - - /// Retruns the current width of the window - int w(); - - /// Returns the current height of the window - int h(); - - SDL_Window *sdl_window(); - -private: - /// SDL main window struct - SDL_Window *window; - - /// Window width - int width; - - /// Window height - int height; +class Window +{ + public: + /*** + * Creates a main window with given \ref title, width \ref w and height \ref h + * + * @param title Title of the window + * @param w Width + * @param h Height + */ + Window(std::string title, int w, int h); + + /** + * Forbids the creation of copies of a window + */ + Window(const Window&) = delete; + Window& operator=(const Window&) = delete; + + /*** + * Destructor. + */ + ~Window(); + + /// Retruns the current width of the window + int w(); + + /// Returns the current height of the window + int h(); + + SDL_Window* sdl_window(); + + private: + /// SDL main window struct + SDL_Window* window; + + /// Window width + int width; + + /// Window height + int height; }; } // namespace advanced_wars -- GitLab