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

Merge branch 'uiInteraction/building' into 'main'

Ui interaction/building

See merge request !24
parents 212f0cd1 db8ddd5e
No related branches found
No related tags found
2 merge requests!29Merge main into box2d to implement physics,!24Ui interaction/building
......@@ -24,7 +24,8 @@ Level::Level(
std::vector<Building> buildings, std::vector<Unit> units, std::vector<Effect> effects)
: 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_state(LevelState::SELECTING_STATE),
m_currentPos(TileMarker(RENDERING_SCALE, 1, 1, m_width, m_height))
{
m_contextMenu.setOptions({"Move", "Info", "Wait"});
......@@ -218,6 +219,12 @@ void Level::render(Engine& engine)
{
m_contextMenu.render(engine);
}
if (m_state == LevelState::RECRUITING_STATE)
{
m_recruitingMenu.render(engine);
}
m_currentPos.render(engine);
}
int Level::addBuilding(Building building)
......@@ -270,31 +277,128 @@ Effect Level::removeEffect(int id)
void Level::handleRecruitingEvent(Engine& engine, SDL_Event& event) {
switch (event.type)
{
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE)
{
m_state = LevelState::MENUACTIVE_STATE;
}
if (event.key.keysym.sym == SDLK_UP || event.key.keysym.sym == SDLK_DOWN)
{
m_recruitingMenu.handleEvent(engine, event);
}
if (event.key.keysym.sym == SDLK_RETURN)
{
Building& b = m_buildings.at(m_selectedBuilding);
UnitFaction u_faction = static_cast<UnitFaction> (b.m_faction);
//show appropriate interface -> provide vector of UnitId
//select UnitId
UnitId u_id = UnitId::INFANTERY;
UnitId unit_id = m_recruitingMenu.getSelectedOption();
if(b.check_money(500)) {
if(b.check_spawn(m_units)){
addUnit(Unit(b.m_x, b.m_y, u_faction, u_id, UnitState::IDLE));
addUnit(Unit(b.m_x, b.m_y, u_faction, unit_id, UnitState::IDLE));
m_state = LevelState::SELECTING_STATE;
m_selectedBuilding = -1;
}
}
}
}}
//*******************helper functions for event Handling*************************************
void Level::handleAttack(std::pair<int, int> tilePos)
{
int targetedUnit = selectUnit(tilePos.first, tilePos.second);
if (targetedUnit >= 0)
{
if (m_units.at(m_selectedUnit).getFaction() == m_units.at(targetedUnit).getFaction())
{
std::cout << "You cannot attack your allies!" << std::endl;
return;
}
Unit& attacking = m_units.at(m_selectedUnit);
Unit& defending = m_units.at(targetedUnit);
attacking.attack(defending);
if (attacking.m_health <= 0)
{
removeUnit(m_selectedUnit);
}
if (defending.m_health <= 0)
{
removeUnit(targetedUnit);
}
m_selectedUnit = -1;
m_state = LevelState::SELECTING_STATE;
}
else
{
std::cout << "No valid target clicked" << std::endl;
}
}
void Level::handleMovement(std::pair<int, int> tilePos)
{
for (auto& [id, unit] : m_units)
{
if (unit.m_x == tilePos.first && unit.m_y == tilePos.second)
{
// unit already at clicked position (maybe even selected unit)
std::cout << "Unit already at clicked position" << std::endl;
return;
}
}
m_units.at(m_selectedUnit).updatePosition(tilePos.first, tilePos.second);
m_selectedUnit = -1;
m_state = LevelState::SELECTING_STATE;
}
void Level::handlePositionMarker(Engine& engine, SDL_Event& event)
{
if (event.key.keysym.sym == SDLK_UP || event.key.keysym.sym == SDLK_DOWN ||
event.key.keysym.sym == SDLK_LEFT || event.key.keysym.sym == SDLK_RIGHT)
{
m_currentPos.handleEvent(engine, event);
}
}
//*******************end helper functions for event Handling*********************************
//************event handler delegates for different level states*****************************
void Level::handleSelectingEvents(Engine& engine, SDL_Event& event)
{
switch (event.type)
{
case SDL_KEYDOWN:
handlePositionMarker(engine, event);
if (event.key.keysym.sym == SDLK_ESCAPE)
{
engine.pushScene(std::make_shared<PauseMenu>(0, nullptr));
}
if (event.key.keysym.sym == SDLK_RETURN)
{
std::pair<int, int> tilePos = m_currentPos.getPosition();
selectEntity(
tilePos.first * 16 * RENDERING_SCALE, tilePos.second * 16 * RENDERING_SCALE);
if (m_selectedUnit >= 0 || m_selectedBuilding >= 0)
{
m_contextMenu.update(
(tilePos.first * 16 + 15) * RENDERING_SCALE,
(tilePos.second * 16 + 15) * RENDERING_SCALE);
if (m_selectedUnit >= 0)
{
m_contextMenu.setOptions({"Move", "Attack", "Info", "Wait"});
}
else
{
m_contextMenu.setOptions({"Train", "Info", "Wait"});
}
m_state = LevelState::MENUACTIVE_STATE;
}
}
break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT)
......@@ -303,6 +407,7 @@ void Level::handleSelectingEvents(Engine& engine, SDL_Event& event)
if (m_selectedUnit >= 0 || m_selectedBuilding >= 0)
{
std::pair<int, int> tilePos = calcTilePos(event.button.x, event.button.y);
m_currentPos.setPosition(tilePos.first, tilePos.second);
m_contextMenu.update(
(tilePos.first * 16 + 15) * RENDERING_SCALE,
(tilePos.second * 16 + 15) * RENDERING_SCALE);
......@@ -375,7 +480,23 @@ void Level::handleMenuActiveEvents(Engine& engine, SDL_Event& event)
if (cmd == "Train")
{
m_state = LevelState::RECRUITING_STATE;
std::pair<int, int> tilePos = m_currentPos.getPosition();
m_recruitingMenu.update(
(tilePos.first * 16 + 15) * RENDERING_SCALE,
(tilePos.second * 16 + 15) * RENDERING_SCALE);
m_recruitingMenu.setOptions({
UnitId::INFANTERY,
UnitId::MECHANIZED_INFANTERY,
UnitId::RECON,
UnitId::APC,
UnitId::ARTILLERY,
UnitId::ANTI_AIR_TANK,
UnitId::ANTI_AIR_MISSILE_LAUNCHER,
UnitId::ROCKET_ARTILLERY,
UnitId::MEDIUM_TANK,
UnitId::NEO_TANK,
UnitId::HEAVY_TANK});
std::cout << "no training here" << std::endl;
}
}
......@@ -390,8 +511,10 @@ void Level::handleMovementEvents(Engine& engine, SDL_Event& event)
switch (event.type)
{
case SDL_KEYDOWN:
handlePositionMarker(engine, event);
if (event.key.keysym.sym == SDLK_RETURN)
{
handleMovement(m_currentPos.getPosition());
}
if (event.key.keysym.sym == SDLK_ESCAPE)
{
......@@ -403,18 +526,8 @@ void Level::handleMovementEvents(Engine& engine, SDL_Event& event)
{
// Bei Movement animation in ANIMATING_STATE gehen
std::pair<int, int> tilePos = calcTilePos(event.button.x, event.button.y);
for (auto& [id, unit] : m_units)
{
if (unit.m_x == tilePos.first && unit.m_y == tilePos.second)
{
// unit already at clicked position (maybe even selected unit)
std::cout << "Unit already at clicked position" << std::endl;
return;
}
}
m_units.at(m_selectedUnit).updatePosition(tilePos.first, tilePos.second);
m_selectedUnit = -1;
m_state = LevelState::SELECTING_STATE;
m_currentPos.setPosition(tilePos.first, tilePos.second);
handleMovement(tilePos);
}
break;
default:
......@@ -427,49 +540,27 @@ void Level::handleAttackingEvents(Engine& engine, SDL_Event& event)
switch (event.type)
{
case SDL_KEYDOWN:
handlePositionMarker(engine, event);
if (event.key.keysym.sym == SDLK_ESCAPE)
{
m_state = LevelState::MENUACTIVE_STATE;
}
if (event.key.keysym.sym == SDLK_RETURN)
{
handleAttack(m_currentPos.getPosition());
}
break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT)
{
std::pair<int, int> tilePos = calcTilePos(event.button.x, event.button.y);
int targetedUnit = selectUnit(tilePos.first, tilePos.second);
if (targetedUnit >= 0)
{
if (m_units.at(m_selectedUnit).getFaction() ==
m_units.at(targetedUnit).getFaction())
{
std::cout << "You cannot attack your allies!" << std::endl;
return;
}
Unit& attacking = m_units.at(m_selectedUnit);
Unit& defending = m_units.at(targetedUnit);
attacking.attack(defending);
if (attacking.m_health <= 0)
{
removeUnit(m_selectedUnit);
}
if (defending.m_health <= 0)
{
removeUnit(targetedUnit);
}
m_selectedUnit = -1;
m_state = LevelState::SELECTING_STATE;
}
else
{
std::cout << "No valid target clicked" << std::endl;
}
m_currentPos.setPosition(tilePos.first, tilePos.second);
handleAttack(tilePos);
}
break;
default:
break;
}
}
//************end event handler delegates for different level states*****************************
} // namespace advanced_wars
......@@ -7,6 +7,8 @@
#include "Tile.hpp"
#include "Unit.hpp"
#include "ui/Contextmenu.hpp"
#include "ui/TileMarker.hpp"
#include "ui/Recruitingmenu.hpp"
#include <SDL.h>
#include <string>
#include <unordered_map>
......@@ -22,7 +24,7 @@ enum class LevelState
ANIMATING_STATE,
MENUACTIVE_STATE,
ATTACKING_STATE,
RECRUITING_STATE
RECRUITING_STATE,
};
/**
......@@ -79,6 +81,7 @@ class Level : public Scene
int m_selectedUnit;
int m_selectedBuilding;
ContextMenu m_contextMenu;
RecruitingMenu m_recruitingMenu;
int m_id;
LevelState m_state;
......@@ -87,11 +90,17 @@ class Level : public Scene
int selectUnit(int tileX, int tileY);
int selectBuilding(int tileX, int tileY);
TileMarker m_currentPos;
void handleSelectingEvents(Engine& engine, SDL_Event& event);
void handleMenuActiveEvents(Engine& engine, SDL_Event& event);
void handleMovementEvents(Engine& engine, SDL_Event& event);
void handleAttackingEvents(Engine& engine, SDL_Event& event);
void handleRecruitingEvent(Engine& engine, SDL_Event& event);
void handleAttack(std::pair<int, int> tilePos);
void handleMovement(std::pair<int, int> tilePos);
void handlePositionMarker(Engine& engine, SDL_Event& event);
};
} // namespace advanced_wars
#include "Recruitingmenu.hpp"
#include <iostream>
#include <SDL_ttf.h>
namespace advanced_wars
{
RecruitingMenu::RecruitingMenu() : m_selectedOption(0), unitNames({
{UnitId::INFANTERY, {"Infantry", 100}},
{UnitId::MECHANIZED_INFANTERY, {"Bazooka", 200}},
{UnitId::RECON, {"Recon", 300}},
{UnitId::APC, {"APC", 400}},
{UnitId::ARTILLERY, {"Artillery", 500}},
{UnitId::ANTI_AIR_TANK, {"AA Tank", 600}},
{UnitId::ANTI_AIR_MISSILE_LAUNCHER, {"Rocket AA", 700}},
{UnitId::ROCKET_ARTILLERY, {"MLRS", 800}},
{UnitId::MEDIUM_TANK, {"Medium Tank", 900}},
{UnitId::NEO_TANK, {"Neo Tank", 1000}},
{UnitId::HEAVY_TANK, {"Heavy Tank", 1100}},
{UnitId::LANDER, {"Lander", 1200}},
{UnitId::CRUISER, {"Cruiser", 1300}},
{UnitId::SUBMARINE, {"Submarine", 1400}},
{UnitId::BATTLESHIP, {"Battleship", 1500}},
{UnitId::TRANSPORT_HELICOPTER, {"Chinook", 1600}},
{UnitId::BATTLE_HELICOPTER, {"Helicopter", 1700}},
{UnitId::FIGHTER, {"Fighter", 1800}},
{UnitId::BOMBER, {"Bomber", 1900}}
}) {
}
void RecruitingMenu::setOptions(const std::vector<UnitId> recruitableUnits) {
std::vector<std::pair<std::string, int>> options;
for (UnitId id : recruitableUnits) {
options.push_back(unitNames.at(id));
cost2UnitId.insert(std::make_pair(unitNames.at(id).second, id));
}
m_options = options;
m_selectedOption = 0;
}
void RecruitingMenu::render(Engine& engine)
{
Spritesheet* spritesheet = engine.getSpritesheet();
if (TTF_Init() == -1)
{
std::cerr << "Failed to initialize TTF: " << TTF_GetError() << std::endl;
return;
}
if (m_options.empty())
{
// TODO handle somehow
return;
}
std::string basePath = SDL_GetBasePath();
std::string relativePath = "res/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 = {m_x, m_y - 3, 150, static_cast<int>(m_options.size() * spacing)};
SDL_RenderFillRect(engine.renderer(), &box);
SDL_SetRenderDrawColor(engine.renderer(), 0, 0, 0, 255);
int i = 0;
for (auto& [render_name, cost] : m_options)
{
//std::pair<std::string, int> unit_option = unitNames.at(cost2UnitId.at(cost));
if(i == m_selectedOption) {
m_selectedId = cost2UnitId.at(cost);
}
SDL_Surface* textSurface = TTF_RenderText_Solid(
font, render_name.c_str(), (i == m_selectedOption) ? yellow : white);
if (!textSurface)
{
continue;
}
SDL_Texture* textTexture = SDL_CreateTextureFromSurface(engine.renderer(), textSurface);
SDL_Rect textRect = {
m_x + 10 + 16, m_y + static_cast<int>(i * spacing), textSurface->w, textSurface->h};
SDL_RenderCopy(engine.renderer(), textTexture, nullptr, &textRect);
SDL_Texture* unit_texture = spritesheet->getUnitTextures()
.at(static_cast<int>(UnitFaction::URED))
.at(static_cast<int>(cost2UnitId.at(cost)))
.at(static_cast<int>(UnitState::IDLE))
.first;
SDL_Rect trgt_rect = {
m_x + 5,
m_y + static_cast<int>(i * spacing),
16,
16
};
SDL_Rect src_rect = {
5,
0,
10,
10
};
SDL_RenderCopy(engine.renderer(), unit_texture, &src_rect, &trgt_rect);
SDL_Surface* costSurface = TTF_RenderText_Solid(
font, std::to_string(cost).c_str(), (i == m_selectedOption) ? yellow : white);
if (!textSurface)
{
continue;
}
SDL_Texture* costTexture = SDL_CreateTextureFromSurface(engine.renderer(), costSurface);
SDL_Rect cost_rect {
m_x + 120 ,
m_y + static_cast<int>(i * spacing),
costSurface->w,
costSurface->h
};
SDL_RenderCopy(engine.renderer(), costTexture, nullptr, &cost_rect);
SDL_DestroyTexture(costTexture);
SDL_FreeSurface(costSurface);
SDL_DestroyTexture(textTexture);
SDL_FreeSurface(textSurface);
i++;
}
TTF_CloseFont(font);
TTF_Quit();
}
void RecruitingMenu::handleEvent(Engine& engine, SDL_Event& event)
{
if (event.type == SDL_KEYDOWN)
{
if (event.key.keysym.sym == SDLK_DOWN)
{
m_selectedOption = (m_selectedOption + 1) % m_options.size();
}
else if (event.key.keysym.sym == SDLK_UP)
{
m_selectedOption = (m_selectedOption - 1 + m_options.size()) % m_options.size();
}
}
}
void RecruitingMenu::update(int x, int y)
{
this->m_x = x;
this->m_y = y;
}
UnitId RecruitingMenu::getSelectedOption(){
return m_selectedId;
}
}//namespace advance_wars
\ No newline at end of file
#pragma once
#include "../Scene.hpp"
#include "../Unit.hpp"
namespace advanced_wars
{
class RecruitingMenu : public Scene {
private:
size_t m_selectedOption;
std::vector<std::pair<std::string, int>> m_options;
int m_x;
int m_y;
const std::unordered_map <UnitId ,std::pair <std::string, int>> unitNames;
std::unordered_map<int, UnitId> cost2UnitId;
UnitId m_selectedId;
void selectSprite();
public:
UnitId getSelectedOption();
void handleEvent(Engine& engine, SDL_Event& event);
void update(int x, int y);
RecruitingMenu();
void setOptions(const std::vector<UnitId> recruitableUnits);
void render(Engine& engine) override;
};
} //namespace advanced_wars
\ No newline at end of file
#include "TileMarker.hpp"
#include <iostream>
namespace advanced_wars
{
TileMarker::TileMarker(int renderingScale, int tileX, int tileY, int levelWidth, int levelHeight)
: m_renderingScale(renderingScale), m_width(16), m_height(16)
{
int tileSize = 16 * renderingScale;
m_x = tileX * tileSize;
m_y = tileY * tileSize + (tileSize - m_height);
m_levelWidth = levelWidth * tileSize;
m_levelHeight = levelHeight * tileSize;
}
void TileMarker::render(Engine& engine)
{
SDL_SetRenderDrawColor(engine.renderer(), 255, 0, 0, 255);
SDL_Rect box = {m_x, m_y, m_width, m_height};
SDL_RenderFillRect(engine.renderer(), &box);
}
void TileMarker::handleEvent(Engine& engine, SDL_Event& event)
{
if (event.type == SDL_KEYDOWN)
{
int newX;
int newY;
switch (event.key.keysym.sym)
{
case SDLK_UP:
newY = m_y - 16 * m_renderingScale;
std::cout << "New Y: " << newY << std::endl;
if (newY <= 0)
{
break;
}
m_y = newY;
break;
case SDLK_DOWN:
newY = m_y + 16 * m_renderingScale;
std::cout << "New Y: " << newY << std::endl;
if (newY >= m_levelHeight)
{
break;
}
m_y = newY;
break;
case SDLK_RIGHT:
newX = m_x + 16 * m_renderingScale;
std::cout << "New X: " << newX << std::endl;
if (newX >= m_levelWidth)
{
break;
}
m_x = newX;
break;
case SDLK_LEFT:
newX = m_x - 16 * m_renderingScale;
std::cout << "New X: " << newX << std::endl;
if (newX <= 0)
{
break;
}
m_x = newX;
break;
default:
break;
}
}
}
std::pair<int, int> TileMarker::getPosition()
{
int tileX = m_x / (16 * m_renderingScale);
int tileY = m_y / (16 * m_renderingScale);
return {tileX, 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);
}
} // namespace advanced_wars
#pragma once
#include "../Scene.hpp"
namespace advanced_wars
{
/**
* @class TileMarker
* @brief Renders a marker on top of a tile to mark current position
*
*/
class TileMarker : public Scene
{
public:
TileMarker(int renderingScale, int tileX, int tileY, int levelWidth, int levelHeight);
void render(Engine& engine) override;
void handleEvent(Engine& engine, SDL_Event& event) override;
std::pair<int, int> getPosition();
void setPosition(int x, int y);
private:
int m_x;
int m_y;
int m_renderingScale;
int m_width;
int m_height;
int m_levelHeight;
int m_levelWidth;
};
} // namespace advanced_wars
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment