diff --git a/res/level.h5 b/res/level.h5 new file mode 100644 index 0000000000000000000000000000000000000000..aa5ba5b60aae9dc4927a4e818e5bb480796ee5a0 Binary files /dev/null and b/res/level.h5 differ diff --git a/src/game/Building.cpp b/src/game/Building.cpp index 8382753499b30666d8e145e8754a7af9faa46bce..fdd4b43ac6a373e638b417139eac71425bcfb074 100644 --- a/src/game/Building.cpp +++ b/src/game/Building.cpp @@ -29,6 +29,11 @@ void Building::render(Engine& engine, int scale) &dst, 0, NULL, SDL_FLIP_NONE); } +BuildingFaction Building::getFaction() +{ + return this->m_faction; +} + void Building::switch_faction(BuildingFaction faction) { diff --git a/src/game/Building.hpp b/src/game/Building.hpp index 8dcb5cdf735640951bacc4b01f6c99e433520b7e..ce0796eaed1fcc374452bc6794861bba1c1e5f28 100644 --- a/src/game/Building.hpp +++ b/src/game/Building.hpp @@ -20,6 +20,8 @@ class Building void render(Engine& engine, int scale); + BuildingFaction getFaction(); + /** Changes the faction to the specified one diff --git a/src/game/Level.cpp b/src/game/Level.cpp index 44f36eec9d43a8883d9dd9b01d0f732c69b1da88..53e8448f7faeb57012079ca6c936256ab3091bac 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -23,11 +23,12 @@ const int RENDERING_SCALE = 3; 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) + std::vector<Building> buildings, std::vector<Unit> units, std::vector<Effect> effects, + std::queue<Player> turnQ) : m_name(name), m_width(width), m_height(height), m_tiles(tiles), m_selectedUnit(-1), m_selectedBuilding(-1), m_contextMenu(ContextMenu()), m_id(0), m_state(LevelState::SELECTING_STATE), - m_currentPos(TileMarker(RENDERING_SCALE, 1, 1, m_width, m_height)) + m_currentPos(TileMarker(RENDERING_SCALE, 1, 1, m_width, m_height)), m_turnQ(turnQ) { m_contextMenu.setOptions({"Move", "Info", "Wait"}); @@ -56,7 +57,7 @@ Level::Level( m_selectedUnit = -1; }; -Level Level::loadLevel(std::string path) +std::shared_ptr<Level> Level::loadLevel(std::string path, Engine& engine) { HighFive::File file(path, HighFive::File::ReadOnly); @@ -76,10 +77,12 @@ Level Level::loadLevel(std::string path) int height = pt.get<int>("level.height"); std::string name = pt.get<std::string>("level.name"); - // create tiles and buildings vector from tiles array + // create tiles, buildings and units vector from tiles array std::vector<Tile> tiles; std::vector<Building> buildings; + std::vector<Unit> units; tiles.reserve(width * height); + bool has_factions[] = {false, false, false, false, false}; for (int i = 0; i < level_tilesarray.size(); i++) { int x = i % width; @@ -90,6 +93,17 @@ Level Level::loadLevel(std::string path) BuildingId building_id = static_cast<BuildingId>((level_tilesarray[i] - 50) % 5); BuildingFaction faction_id = static_cast<BuildingFaction>((level_tilesarray[i] - 50) / 5); + if (building_id == BuildingId::HEADQUARTER) + { + int index = static_cast<int>(faction_id); + if (!has_factions[index]) + { + units.push_back(Unit( + x, y, static_cast<UnitFaction>(faction_id), UnitId::INFANTERY, + UnitState::UNAVAILABLE, engine.getUnitConfig())); + } + has_factions[static_cast<int>(faction_id)] = true; + } buildings.push_back(Building(x, y, building_id, faction_id)); } else @@ -99,8 +113,21 @@ Level Level::loadLevel(std::string path) } } - return Level(name, width, height, tiles, buildings, {}, {}); -}; + // create turnQ from has_factions array + std::queue<Player> turnQ; + for (int i = 0; i < 5; i++) + { + if (has_factions[i]) + { + turnQ.push(Player(2000, static_cast<PlayerFaction>(i))); + } + } + + Level level(name, width, height, tiles, buildings, units, std::vector<Effect>{}, turnQ); + + level.m_turnQ.front().startTurn(level.m_units, level.m_buildings); + return std::make_shared<Level>(level); +} std::pair<int, int> Level::calcTilePos(int mouseX, int mouseY) { @@ -371,6 +398,19 @@ Effect Level::removeEffect(int id) return value; } +void Level::changeTurn() +{ + Player temp = m_turnQ.front(); + + temp.endTurn(m_units); + + // Cycle Player at end of queue + m_turnQ.pop(); + m_turnQ.push(temp); + + m_turnQ.front().startTurn(m_units, m_buildings); +} + void Level::handleRecruitingEvent(Engine& engine, SDL_Event& event) { diff --git a/src/game/Level.hpp b/src/game/Level.hpp index c4d06287456c031e2a2bf24e9351e8de80ceb1f8..6e2aeea2722c846eaa623af0400d3fc371843528 100644 --- a/src/game/Level.hpp +++ b/src/game/Level.hpp @@ -3,6 +3,7 @@ #include "Building.hpp" #include "Effect.hpp" #include "Engine.hpp" +#include "Player.hpp" #include "Scene.hpp" #include "Tile.hpp" #include "Unit.hpp" @@ -11,6 +12,7 @@ #include "ui/TileMarker.hpp" #include <SDL.h> #include <array> +#include <queue> #include <string> #include <unordered_map> #include <unordered_set> @@ -75,9 +77,10 @@ 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>); + std::vector<Building> buildings, std::vector<Unit> units, std::vector<Effect> effects, + std::queue<Player> turnQ); - static Level loadLevel(std::string path); + static std::shared_ptr<Level> loadLevel(std::string path, Engine& engine); void render(Engine& engine); @@ -126,12 +129,14 @@ class Level : public Scene std::unordered_map<int, Building> m_buildings; std::unordered_map<int, Unit> m_units; std::unordered_map<int, Effect> m_effects; - int m_selectedUnit; - int m_selectedBuilding; - ContextMenu m_contextMenu; - RecruitingMenu m_recruitingMenu; - int m_id; - LevelState m_state; + std::queue<Player> m_turnQ; + + int m_selectedUnit; + int m_selectedBuilding; + ContextMenu m_contextMenu; + RecruitingMenu m_recruitingMenu; + int m_id; + LevelState m_state; std::pair<int, int> calcTilePos(int mouseX, int mouseY); void selectEntity(int x, int y); @@ -148,6 +153,9 @@ class Level : public Scene bool clickCheckLeft(int mouseX, int mouseY); bool clickCheckRight(int mouseX, int mouseY); + + void changeTurn(); + bool m_showReachableTiles; bool m_showAttackableTiles; diff --git a/src/game/Player.cpp b/src/game/Player.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9bd7bad8b46ea9b367b91d59895cd111443da8f1 --- /dev/null +++ b/src/game/Player.cpp @@ -0,0 +1,147 @@ +#include "Player.hpp" +#include <iostream> + +namespace advanced_wars +{ + +Player::Player(int money, PlayerFaction faction) + : m_money(money), m_alive(true), m_activeTurn(false), m_faction(faction) +{ +} + +Player::~Player() {} + +void Player::startTurn( + std::unordered_map<int, Unit>& lvUnits, std::unordered_map<int, Building>& lvBuildings) +{ + for (auto& [id, unit] : lvUnits) + { + switch (m_faction) + { + case PlayerFaction::RED: + if (unit.getFaction() == UnitFaction::URED) + { + unit.setState(UnitState::IDLE); + } + break; + case PlayerFaction::BLUE: + if (unit.getFaction() == UnitFaction::UBLUE) + { + unit.setState(UnitState::IDLE); + } + break; + case PlayerFaction::YELLOW: + if (unit.getFaction() == UnitFaction::UYELLOW) + { + unit.setState(UnitState::IDLE); + } + break; + case PlayerFaction::GREEN: + if (unit.getFaction() == UnitFaction::UGREEN) + { + unit.setState(UnitState::IDLE); + } + break; + case PlayerFaction::PURPLE: + if (unit.getFaction() == UnitFaction::UPURPLE) + { + unit.setState(UnitState::IDLE); + } + break; + + default: + break; + } + } + + int underControl = 0; + + for (auto& [id, building] : lvBuildings) + { + switch (m_faction) + { + case PlayerFaction::RED: + if (building.getFaction() == BuildingFaction::URED) + { + underControl++; + } + break; + case PlayerFaction::BLUE: + if (building.getFaction() == BuildingFaction::UBLUE) + { + underControl++; + } + break; + case PlayerFaction::YELLOW: + if (building.getFaction() == BuildingFaction::UYELLOW) + { + underControl++; + } + break; + case PlayerFaction::GREEN: + if (building.getFaction() == BuildingFaction::UGREEN) + { + underControl++; + } + break; + case PlayerFaction::PURPLE: + if (building.getFaction() == BuildingFaction::UPURPLE) + { + underControl++; + } + break; + + default: + break; + } + } + + m_money += 1000 * underControl; + + m_activeTurn = true; +} + +void Player::endTurn(std::unordered_map<int, Unit>& lvUnits) +{ + for (auto& [id, unit] : lvUnits) + { + switch (m_faction) + { + case PlayerFaction::RED: + if (unit.getFaction() == UnitFaction::URED) + { + unit.setState(UnitState::UNAVAILABLE); + } + break; + case PlayerFaction::BLUE: + if (unit.getFaction() == UnitFaction::UBLUE) + { + unit.setState(UnitState::UNAVAILABLE); + } + break; + case PlayerFaction::YELLOW: + if (unit.getFaction() == UnitFaction::UYELLOW) + { + unit.setState(UnitState::UNAVAILABLE); + } + break; + case PlayerFaction::GREEN: + if (unit.getFaction() == UnitFaction::UGREEN) + { + unit.setState(UnitState::UNAVAILABLE); + } + break; + case PlayerFaction::PURPLE: + if (unit.getFaction() == UnitFaction::UPURPLE) + { + unit.setState(UnitState::UNAVAILABLE); + } + break; + + default: + break; + } + } + m_activeTurn = false; +} +} // namespace advanced_wars \ No newline at end of file diff --git a/src/game/Player.hpp b/src/game/Player.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f77bb6c36d30953d8c8ab1c89d5708129ac91b4e --- /dev/null +++ b/src/game/Player.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "Building.hpp" +#include "Unit.hpp" +#include <unordered_map> + +namespace advanced_wars +{ + +enum class PlayerFaction +{ + RED, + BLUE, + YELLOW, + GREEN, + PURPLE +}; + +class Player +{ + private: + int m_money; + bool m_alive; + bool m_activeTurn; + PlayerFaction m_faction; + + public: + Player(int money, PlayerFaction faction); + ~Player(); + + /** + * Sets all units of the players faction to idle, adds money for each building under the + * players control and sets them as the active player. + * + * @param lvUnits All current units of a level + * @param lvBuildings All buildings of a level + */ + void startTurn( + std::unordered_map<int, Unit>& lvUnits, std::unordered_map<int, Building>& lvBuildings); + + /** + * Sets all units of the players faction to unavailable and sets them as no longer being the + * active player. + * + * @param lvUnits All current units of a level + */ + void endTurn(std::unordered_map<int, Unit>& lvUnits); +}; + +} // namespace advanced_wars diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 739c75b739afd9d2a5b00451d4370fcfed211ded..f5d85a3a8c1846be3736f0a1c02ccc0a772572b9 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -42,8 +42,6 @@ Unit::Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state, Config m_secondaryWeapon = Weapon(config.getUnitSecondaryWeapon(id), secondaryDamage); } - - void Unit::render(Engine& engine, int scale) { Spritesheet* spritesheet = engine.getSpritesheet(); @@ -149,9 +147,9 @@ void Unit::attack(Unit& enemy) int Unit::calculateDamage(Unit& target) { // Pointers to Weapon objects - Weapon* primaryWeapon = &m_primaryWeapon; + Weapon* primaryWeapon = &m_primaryWeapon; Weapon* secondaryWeapon = &m_secondaryWeapon; - + // Find the corresponding damage values auto primary_damage_it = primaryWeapon->m_damage.find(target.m_id); auto secondary_damage_it = secondaryWeapon->m_damage.find(target.m_id); @@ -181,8 +179,6 @@ int Unit::calculateDamage(Unit& target) return damage_value; } - - void Unit::performAttack(Unit& target, int damage) { int effective_damage = damage * (static_cast<float>(m_health) / m_maxHealth); @@ -244,7 +240,7 @@ std::vector<Unit*> Unit::getUnitsInRangeWithDamagePotential(const std::vector<Un std::vector<Unit*> unitsInRangeWithDamage; for (Unit* unit : allUnits) - { //Iterate over all units + { // Iterate over all units // except itself if (unit == this) { @@ -254,7 +250,6 @@ std::vector<Unit*> Unit::getUnitsInRangeWithDamagePotential(const std::vector<Un int distanceX = std::abs(unit->m_x - m_x); int distanceY = std::abs(unit->m_y - m_y); - int distance = distanceX + distanceY; if (distance >= m_minRange && distance <= m_maxRange) { @@ -285,11 +280,6 @@ std::vector<Unit*> Unit::getUnitsInRangeWithDamagePotential(const std::vector<Un return unitsInRangeWithDamage; } -UnitFaction Unit::getFaction() -{ - return this->m_faction; -} - void Unit::renderHP(Engine& engine, int scale) { Spritesheet* spritesheet = engine.getSpritesheet(); @@ -324,4 +314,14 @@ void Unit::renderHP(Engine& engine, int scale) } } +UnitFaction Unit::getFaction() +{ + return this->m_faction; +} + +void Unit::setState(UnitState state) +{ + this->m_state = state; +} + } // namespace advanced_wars \ No newline at end of file diff --git a/src/game/Unit.hpp b/src/game/Unit.hpp index 2202f9375390bbd488c381230a8013ab9d179996..a2495e2307ff37871cf890cc08692ef3d8f34453 100644 --- a/src/game/Unit.hpp +++ b/src/game/Unit.hpp @@ -10,6 +10,7 @@ namespace advanced_wars { + class Engine; class Config; @@ -120,6 +121,10 @@ class Unit */ void on_left_click(SDL_Event event); + UnitFaction getFaction(); + + void setState(UnitState state); + /** * Retrieves units within range that this unit can deal damage to. * Considers all units provided in 'allUnits', excluding itself, and checks movement and @@ -130,8 +135,6 @@ class Unit */ std::vector<Unit*> getUnitsInRangeWithDamagePotential(const std::vector<Unit*>& allUnits); - UnitFaction getFaction(); - private: UnitFaction m_faction; // The faction to which this unit belongs. UnitId m_id; // The identifier for the unit type. diff --git a/src/game/ui/Menu.cpp b/src/game/ui/Menu.cpp index ca3cf639c8dbb5a6c141e8b71bfec58884d3e112..770a153d058d799165e88e6fc9dbae6e6f79afb1 100644 --- a/src/game/ui/Menu.cpp +++ b/src/game/ui/Menu.cpp @@ -1,7 +1,7 @@ #include "Menu.hpp" #include "../Building.hpp" -#include "../Level.hpp" #include "../Config.hpp" +#include "../Level.hpp" #include "../Spritesheet.hpp" #include "../Tile.hpp" #include "../Unit.hpp" @@ -126,7 +126,6 @@ void Menu::handleEvent(Engine& engine, SDL_Event& event) { std::cout << "Starting game..." << std::endl; - // Construct a level std::vector<Tile> tiles; for (int y = 0; y < 20; y++) @@ -199,10 +198,11 @@ void Menu::handleEvent(Engine& engine, SDL_Event& event) 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); + // std::shared_ptr<Level> level = + // std::make_shared<Level>("Osnabrück", 20, 20, tiles, buildings, units, + // effects, std::queue<Player>{}); - engine.pushScene(level); + engine.pushScene(Level::loadLevel("../res/level.h5", engine)); } else if (m_options[m_selectedOption] == "Options") { diff --git a/src/game/ui/TileMarker.cpp b/src/game/ui/TileMarker.cpp index cfffcc6c157215a117ecf8989374bbef32b02a75..1ad9d6a8dea68308485f4c6b877ed28f875f3b65 100644 --- a/src/game/ui/TileMarker.cpp +++ b/src/game/ui/TileMarker.cpp @@ -62,7 +62,7 @@ void TileMarker::handleEvent(Engine& engine, SDL_Event& event) newX = m_x - 16 * m_renderingScale; std::cout << "New X: " << newX << std::endl; - if (newX <= 0) + if (newX < 0) { break; } @@ -82,7 +82,8 @@ std::pair<int, int> TileMarker::getPosition() return {tileX, tileY}; } -void TileMarker::setPosition(int tileX, int tileY){ +void TileMarker::setPosition(int tileX, int tileY) +{ m_x = tileX * 16 * m_renderingScale; m_y = tileY * 16 * m_renderingScale + (16 * m_renderingScale - m_height); }