Skip to content
Snippets Groups Projects
Commit fd94bc7a authored by Frederik Alexander Keens's avatar Frederik Alexander Keens
Browse files

Merge branch 'units' into 'main'

Merge units into main

See merge request !15
parents b58dcb12 9834ecf8
No related branches found
No related tags found
1 merge request!15Merge units into main
......@@ -7,6 +7,7 @@
#include "ui/pausemenu.hpp"
#include "unit.hpp"
#include <SDL.h>
#include <algorithm>
#include <iostream>
#include <string>
......@@ -43,14 +44,165 @@ Level::Level(
}
};
const int RENDERING_SCALE = 3;
bool Level::click_check_left(int tileX, int tileY)
{
if (selectUnit(tileX, tileY))
{
return true;
}
if (selectBuilding(tileX, tileY))
{
return true;
}
return false;
}
bool Level::click_check_right(int tileX, int tileY)
{
if (target_unit(tileX, tileY))
{
return true;
}
return false;
}
bool Level::selectUnit(int tileX, int tileY)
{
// std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl;
for (auto& [id, unit] : units)
{
if (unit.x == tileX && unit.y == tileY)
{
// std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl;
selectedUnit = id;
return true;
}
}
return false;
}
bool Level::target_unit(int tileX, int tileY)
{
// std::cout << "tileX:" << tileX << "tileX:" << tileY << std::endl;
for (auto& [id, unit] : units)
{
if (unit.x == tileX && unit.y == tileY)
{
// std::cout << "unitX:" << unit.x << "unitY:" << unit.y << std::endl;
targetedUnit = id;
return true;
}
}
return false;
}
bool Level::selectBuilding(int tileX, int tileY)
{
for (auto& [id, building] : buildings)
{
if (building.x == tileX && building.y == tileY)
{
// std::cout << "X:" << unit.x << "Y:" << unit.y << std::endl;
selectedBuilding = id;
return true;
}
}
return false;
}
void Level::handleEvent(Engine& engine, SDL_Event& event)
{
switch (event.type)
{
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT)
{
int tileX = event.button.x / (16 * RENDERING_SCALE);
int tileY = event.button.y / (16 * RENDERING_SCALE);
if (click_check_left(tileX, tileY))
{
if (selectedUnit > -1)
{
units.at(selectedUnit).on_left_click(event);
}
if (selectedBuilding > -1)
{
// building stuff
}
}
else
{
std::cout << "Neither building nor unit clicked!" << std::endl;
selectedUnit = -1;
selectedBuilding = -1;
}
}
else if (event.button.button == SDL_BUTTON_RIGHT)
{
if (selectedUnit > -1)
{
int tileX = event.button.x / (16 * RENDERING_SCALE);
int tileY = event.button.y / (16 * RENDERING_SCALE);
if (click_check_right(tileX, tileY))
{
units.at(selectedUnit).attack(&(units.at(targetedUnit)));
if (units.at(selectedUnit).health <= 0)
{
remove_unit(selectedUnit);
}
}
else
{
units.at(selectedUnit).update_position(tileX, tileY);
}
}
else
{
std::cout << "No unit selected! " << std::endl;
}
}
}
}
void Level::render(Engine* engine)
{
const int RENDERING_SCALE = 3;
// Iterate over all events
while (!engine->events().empty())
{
handleEvent(engine, engine->events().at(0));
// handleEvent(engine, engine->events().at(0));
handleEvent(*engine, engine->events().at(0));
engine->events().pop_front();
}
......
......@@ -41,6 +41,8 @@ class Level : public Scene
Effect remove_effect(int id);
void handleEvent(Engine& engine, SDL_Event& event);
private:
std::string name;
int width;
......@@ -50,6 +52,15 @@ class Level : public Scene
std::unordered_map<int, Building> buildings;
std::unordered_map<int, Unit> units;
std::unordered_map<int, Effect> effects;
int selectedUnit;
int targetedUnit;
int selectedBuilding;
bool selectUnit(int tileX, int tileY);
bool target_unit(int tileX, int tileY);
bool selectBuilding(int tileX, int tileY);
bool click_check_left(int mouseX, int mouseY);
bool click_check_right(int mouseX, int mouseY);
ContextMenu context_menu;
bool context_menu_active;
......
......@@ -5,10 +5,7 @@
namespace advanced_wars
{
Tile::Tile(TileId id, int x, int y)
: id(id), x(x), y(y) {
};
Tile::Tile(TileId id, int x, int y) : id(id), x(x), y(y) {}
void Tile::render(Engine* engine, int scale)
{
......
#include "unit.hpp"
#include <iostream>
namespace advanced_wars
{
Unit::Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state)
: x(x), y(y), faction(faction), id(id), state(state) {
};
: x(x), y(y), faction(faction), id(id), state(state), max_health(100)
{
// das ist nur für Testzwecke
if (id == UnitId::INFANTERY)
{
secondary_weapon = Weapon(
"Machine-Gun", {
{UnitId::INFANTERY, 55}
});
}
health = max_health;
}
void Unit::render(Engine* engine, int scale)
{
......@@ -69,4 +79,141 @@ void Unit::render(Engine* engine, int scale)
}
}
void Unit::attack(Unit* enemy)
{
// Angenommen, primary_weapon und secondary_weapon wurden bereits korrekt
// initialisiert
auto primary_weapon_damage_it = primary_weapon.damage.find(enemy->id);
auto secondary_weapon_damage_it = secondary_weapon.damage.find(enemy->id);
int attacker_damage_value = 0;
// Die Waffe mit dem höchsten Schaden wählen
if (secondary_weapon_damage_it != secondary_weapon.damage.end())
{
attacker_damage_value = secondary_weapon_damage_it->second;
}
if (primary_weapon_damage_it != primary_weapon.damage.end())
{
if (primary_weapon_damage_it->second > attacker_damage_value)
{
// Munitionsabzug sollte hier erfolgen, falls zutreffend
attacker_damage_value = primary_weapon_damage_it->second;
}
}
if (attacker_damage_value == 0)
{
std::cout << "No damage value found for attack from unit " << static_cast<int>(id)
<< " against unit " << static_cast<int>(enemy->id) << std::endl;
}
else
{
int off_damage = attacker_damage_value * (static_cast<float>(health) / max_health);
enemy->health -= off_damage;
enemy->health = std::max(
0,
enemy->health); // Sicherstellen, dass die Gesundheit nicht negativ wird
std::cout << "Enemy health after attack: " << enemy->health << std::endl;
// Prüfen, ob der Gegner noch am Leben ist um zurückzuschlagen
if (enemy->health > 0)
{
// Weapon tables for the defender
auto defender_primary_weapon_damage_it = enemy->primary_weapon.damage.find(id);
auto defender_secondary_weapon_damage_it = enemy->secondary_weapon.damage.find(id);
int defender_damage_value = 0; // Declare outside for later use
// Determine the damage value for the defender
if (defender_secondary_weapon_damage_it != enemy->secondary_weapon.damage.end())
{
defender_damage_value = defender_secondary_weapon_damage_it->second;
}
if (defender_primary_weapon_damage_it != enemy->primary_weapon.damage.end())
{
if (defender_primary_weapon_damage_it->second > defender_damage_value)
{
// Munitionsabzug für primäre Waffe, falls zutreffend
defender_damage_value = defender_primary_weapon_damage_it->second;
}
}
// If a valid damage value was determined for retaliation
if (defender_damage_value > 0)
{
int def_damage = static_cast<int>(
defender_damage_value * static_cast<float>(enemy->health) / enemy->max_health);
this->health -= def_damage;
this->health = std::max(0, this->health); // Safeguard against negative health
std::cout << "Ally health after retaliation: " << this->health << std::endl;
}
}
}
}
void Unit::update_position(int posX, int posY)
{
calc_state(posX, posY);
this->x = posX;
this->y = posY;
}
void Unit::calc_state(int posX, int posY)
{
int deltaX = this->x - posX;
int deltaY = this->y - posY;
if (deltaX == 0 && deltaY == 0)
{
// Unit is already at the target position
return;
}
if (abs(deltaX) >= abs(deltaY))
{
if (deltaX > 0)
{
this->state = advanced_wars::UnitState::MOVEMENTLEFT;
}
else
{
this->state = advanced_wars::UnitState::MOVEMENTRIGHT;
}
}
else
{
if (deltaY > 0)
{
this->state = advanced_wars::UnitState::MOVEMENTUP;
}
else
{
this->state = advanced_wars::UnitState::MOVEMENTDOWN;
}
}
}
void Unit::on_left_click(SDL_Event event)
{
std::cout << "Left-button pressed on unit: " << this->health << std::endl;
}
bool Unit::inRange(Unit* enemy)
{
if (this->x == enemy->x)
{
return abs(this->y - enemy->y) <= this->range;
}
else if (this->y == enemy->y)
{
return abs(this->x - enemy->x) <= this->range;
}
return false;
}
} // namespace advanced_wars
\ No newline at end of file
#pragma once
#include "engine.hpp"
#include "weapon.hpp"
#include <optional>
#include <unordered_map>
namespace advanced_wars
{
......@@ -57,19 +60,80 @@ enum class MovementType
LANDER = 5,
};
using MatchupTable = std::unordered_map<UnitId, std::unordered_map<UnitId, int>>;
class Unit
{
public:
int x;
int y;
int health; // health equals max_health at construction
Unit(int x, int y, UnitFaction faction, UnitId id, UnitState state);
~Unit()
{
// Assuming that the destruktion of a unit triggers events
}
void render(Engine* engine, int scale);
/*
Check if attacker is in Range to initiate combat
TODO: This should probably tie back into rendering the units differently
If a unit is selected, it should call inRange on all other enemy units on the field
*/
bool inRange(Unit* enemy);
/*
The attacker will move towards the defender and thus initiate combat
@params Takes a reference to the defender
Will Update the health for both units
Attacker deals damage to the defender first
*/
void attack(Unit* enemy);
/*
@params Takes the desired position of the unit and updates its values
This will teleport the unit, there is no smooth transition between tiles
*/
void update_position(int posX, int posY);
/*
This function needs to be able to determine the possible movement-paths the unit can take
MUST take into consideration that different units behave differently on certain terrain
MUST show all movements possible
*/
void calculate_movement();
void calc_state(int posX, int posY);
/*
This function will be called by an external event-handler, eventually.
It should start displaying standard unit information, such as UI and move_range
*/
void on_left_click(SDL_Event event);
private:
int x;
int y;
UnitFaction faction;
UnitId id;
UnitState state;
int max_health; // max_health required for damage_scaling
int range;
int fuel;
int max_fuel;
bool has_moved;
bool has_attacked;
bool is_selected;
bool is_targeted;
Weapon secondary_weapon;
Weapon primary_weapon;
int ammo;
};
} // namespace advanced_wars
\ No newline at end of file
#include "weapon.hpp"
namespace advanced_wars
{
Weapon::Weapon() : name(""), damage() {}
Weapon::Weapon(const std::string& weaponName, const std::unordered_map<UnitId, int>& damageValues)
: name(weaponName), damage(damageValues)
{
}
// Funktion zum Hinzufügen von Schadenswerten
void Weapon::addDamageValue(UnitId unitId, int value)
{
damage[unitId] = value;
}
// Funktion zum Abrufen eines Schadenswertes
int Weapon::getDamageValue(UnitId unitId) const
{
auto it = damage.find(unitId);
if (it != damage.end())
{
return it->second;
}
return 0; // oder ein Fehlerwert
}
} // namespace advanced_wars
\ No newline at end of file
#pragma once
#include <string>
#include <unordered_map>
namespace advanced_wars
{
enum class UnitId;
class Weapon
{
public:
// Konstruktoren
Weapon();
Weapon(const std::string& weaponName, const std::unordered_map<UnitId, int>& damageValues);
// Methode, um einen Schadenswert hinzuzufügen
void addDamageValue(UnitId unitId, int value);
// Methode, um einen Schadenswert abzurufen
int getDamageValue(UnitId unitId) const;
// Name der Waffe
std::string name;
// Schadenstabelle
std::unordered_map<UnitId, int> damage;
};
} // namespace advanced_wars
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment