Skip to content
Snippets Groups Projects
Commit 5400f688 authored by Frederik's avatar Frederik
Browse files

Fix problems from units branch merge conflicts

parent 07726a73
Branches
No related tags found
1 merge request!15Merge units into main
---
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
......@@ -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()
......@@ -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:
......
# 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
File added
src/assets/main_background.png

2.57 KiB

#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){};
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();
......@@ -22,8 +24,7 @@ void Building::render(Engine &engine, int scale) {
dst.h = spritesheet->get_building_height() * scale;
SDL_RenderCopyEx(
engine.renderer(),
spritesheet->get_building_textures()[static_cast<int>(faction)], &src,
engine->renderer(), spritesheet->get_building_textures()[static_cast<int>(faction)], &src,
&dst, 0, NULL, SDL_FLIP_NONE);
}
......
......@@ -3,9 +3,11 @@
#include "engine.hpp"
#include "scene.hpp"
namespace advanced_wars {
namespace advanced_wars
{
enum class BuildingFaction {
enum class BuildingFaction
{
RED = 0,
BLUE = 1,
YELLOW = 2,
......@@ -14,7 +16,8 @@ enum class BuildingFaction {
NEUTRAL = 5,
};
enum class BuildingId {
enum class BuildingId
{
HEADQUARTER = 0,
CITY = 1,
FACTORY = 2,
......@@ -22,7 +25,8 @@ enum class BuildingId {
SATELLITE = 4,
};
class Building {
class Building
{
public:
Building(int x, int y, BuildingId id, BuildingFaction faction);
......@@ -31,7 +35,7 @@ public:
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
......@@ -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){
};
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
......@@ -2,9 +2,11 @@
#include "engine.hpp"
namespace advanced_wars {
namespace advanced_wars
{
enum class EffectId {
enum class EffectId
{
LAND_EXPLOSION = 0,
AIR_EXPLOSION = 1,
NAVAL_EXPLOSION = 2,
......@@ -12,11 +14,14 @@ enum class EffectId {
SUBMARINE_APPEAR = 4
};
class Effect {
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;
......
#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);
if (sdl_renderer == nullptr)
{
throw std::runtime_error("SDL could not generate renderer: " + std::string(SDL_GetError()));
}
}
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()));
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();
}
}
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_scene(Scene &scene) { this->scene = &scene; }
return tmp;
}
void Engine::set_spritesheet(Spritesheet &spritesheet) {
void Engine::set_spritesheet(Spritesheet& spritesheet)
{
this->spritesheet = &spritesheet;
}
Spritesheet *Engine::get_spritesheet() { return spritesheet.value(); }
void Engine::pump() {
void Engine::pump()
{
SDL_Event e;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT)
{
this->quit = true;
} else {
this->events.push_back(e);
}
else
{
this->_events.push_back(e);
}
}
}
bool Engine::exited() { return this->quit; }
int Engine::get_stage() { return this->stage; }
void Engine::exit()
{
this->quit = true;
}
void Engine::render() {
if (SDL_RenderClear(this->sdl_renderer) != 0) {
throw std::runtime_error("Could not clear renderer: " +
std::string(SDL_GetError()));
bool Engine::exited()
{
return this->quit;
}
if (!scene.has_value()) {
return;
void Engine::render()
{
if (SDL_RenderClear(this->sdl_renderer) != 0)
{
throw std::runtime_error("Could not clear renderer: " + std::string(SDL_GetError()));
}
stage = SDL_GetTicks() / 300;
std::shared_ptr<Scene> currentScene = scenes.back();
this->scene.value()->render(*this, this->events);
currentScene->render(this);
SDL_RenderPresent(this->sdl_renderer);
}
SDL_Renderer *Engine::renderer() { return this->sdl_renderer; }
int Engine::get_stage()
{
return SDL_GetTicks() / 300;
}
Spritesheet* Engine::get_spritesheet()
{
return spritesheet.value();
}
SDL_Renderer* Engine::renderer()
{
return this->sdl_renderer;
}
Engine::~Engine() {
Engine::~Engine()
{
SDL_DestroyRenderer(sdl_renderer);
IMG_Quit();
SDL_Quit();
......
#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 {
class Engine
{
public:
Engine(Window& window);
......@@ -22,9 +29,17 @@ public:
bool exited();
void exit();
void pump();
void set_scene(Scene &scene);
void push_scene(std::shared_ptr<Scene> scene);
std::optional<std::shared_ptr<Scene>> pop_scene();
void return_to_menu();
std::deque<SDL_Event>& events();
void set_spritesheet(Spritesheet& spritesheet);
......@@ -41,9 +56,9 @@ public:
private:
Window& window;
SDL_Renderer* sdl_renderer;
std::optional<Scene *> scene;
std::vector<std::shared_ptr<Scene>> scenes;
std::optional<Spritesheet*> spritesheet;
std::vector<SDL_Event> events;
std::deque<SDL_Event> _events;
bool quit;
int stage;
};
......
#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())
{
......@@ -59,14 +77,14 @@ namespace advanced_wars
{
// 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;
selectedUnit = &unit;
selectedUnit = id;
return true;
}
}
......@@ -78,14 +96,14 @@ namespace advanced_wars
{
// 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;
targetedUnit = &unit;
targetedUnit = id;
return true;
}
}
......@@ -96,13 +114,13 @@ namespace advanced_wars
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;
selectedBuilding = id;
return true;
}
}
......@@ -125,12 +143,12 @@ namespace advanced_wars
if (click_check_left(tileX, tileY))
{
if (selectedUnit)
if (selectedUnit > -1)
{
selectedUnit->on_left_click(event, units);
units.at(selectedUnit).on_left_click(event);
}
if (selectedBuilding)
if (selectedBuilding > -1)
{
// building stuff
}
......@@ -139,14 +157,14 @@ namespace advanced_wars
{
std::cout << "Neither building nor unit clicked!" << std::endl;
selectedUnit = nullptr;
selectedBuilding = nullptr;
selectedUnit = -1;
selectedBuilding = -1;
}
}
else if (event.button.button == SDL_BUTTON_RIGHT)
{
if (selectedUnit)
if (selectedUnit > -1)
{
int tileX = event.button.x / (16 * RENDERING_SCALE);
......@@ -155,18 +173,17 @@ namespace advanced_wars
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());
if (units.at(selectedUnit).health <= 0)
{
remove_unit(selectedUnit);
}
}
else
{
selectedUnit->update_position(tileX, tileY);
units.at(selectedUnit).update_position(tileX, tileY);
}
}
else
......@@ -178,16 +195,15 @@ namespace advanced_wars
}
}
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
......@@ -197,29 +213,130 @@ namespace advanced_wars
}
// Buildings
for (Building &building : buildings)
for (auto& [id, building] : buildings)
{
building.render(engine, RENDERING_SCALE);
}
// Units
for (Unit &unit : units)
for (auto& [id, unit] : units)
{
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);
}
}
// Set background color for renderer
if (SDL_SetRenderDrawColor(engine.renderer(), 255, 0, 0, 0))
// Remove finished effects after iteration
for (int id : effects_to_remove)
{
std::cout << "Could not set render draw color: " << SDL_GetError()
<< std::endl;
this->remove_effect(id);
}
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)
{
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
\ No newline at end of file
......@@ -5,23 +5,41 @@
#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 {
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>);
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 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);
......@@ -29,19 +47,25 @@ 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;
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
#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));
}
}
// 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);
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("SDL could not initialize: " + std::string(SDL_GetError()));
}
// 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()) {
while (!engine.exited())
{
engine.pump();
engine.render();
}
......
#pragma once
#include "engine.hpp"
#include <SDL.h>
#include <vector>
namespace advanced_wars {
namespace advanced_wars
{
// Forward declaration
class Engine;
class Scene {
class Scene
{
public:
virtual void render(Engine &engine, std::vector<SDL_Event> &events) = 0;
virtual void render(Engine* engine) = 0;
};
} // namespace advanced_wars
/**
* 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,14 +17,17 @@
#include <string>
#include <vector>
namespace advanced_wars {
namespace advanced_wars
{
Spritesheet::Spritesheet(std::string path, Engine &engine) {
Spritesheet::Spritesheet(std::string path, Engine& engine)
{
HighFive::File file(path, HighFive::File::ReadOnly);
// Tiles
std::vector<std::string> tiles({"plain",
std::vector<std::string> tiles(
{"plain",
"water",
"forest",
"mountain",
......@@ -48,7 +58,9 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
"cliff_inverse_corner_bottom_left",
"cliff_inverse_corner_bottom_right"});
for (size_t tile_idx = 0; tile_idx < tiles.size(); tile_idx++) {
// 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;
......@@ -56,9 +68,13 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
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++) {
// 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);
......@@ -72,21 +88,20 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND);
if (tmp == nullptr) {
if (tmp == nullptr)
{
throw std::runtime_error(
"Fehler beim Erstellen der Textur für die Units: " +
std::string(SDL_GetError()));
"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) {
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()));
"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()));
tile_textures.push_back(std::pair<SDL_Texture*, int>(tmp, tile_frames.size()));
}
this->tile_width = 16;
......@@ -96,7 +111,9 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
std::vector<std::string> building_factions(
{"red", "blue", "yellow", "green", "purple", "neutral"});
for (std::string faction : building_factions) {
// every sub data set of buildings
for (std::string faction : building_factions)
{
HighFive::DataSet buildings_ds = file.getDataSet("buildings/" + faction);
std::vector<std::vector<std::vector<uint32_t>>> buildings_frames;
......@@ -105,13 +122,16 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
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++) {
// 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);
building_buffer.at(index) =
buildings_frames.at(n).at(32 - y - 1).at(x);
building_buffer.at(index) = buildings_frames.at(n).at(32 - y - 1).at(x);
}
}
}
......@@ -122,18 +142,19 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND);
if (tmp == nullptr) {
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, building_buffer.data(),
buildings_frames.size() * 16 * sizeof(int32_t)) !=
0) {
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()));
"Fehler beim updaten der Textur für die Buildings: " + std::string(SDL_GetError()));
}
this->building_textures.push_back(tmp);
......@@ -143,35 +164,35 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
this->building_height = 32;
// Units
std::vector<std::string> unit_factions(
{"red", "blue", "green", "yellow", "purple"});
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",
{"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"});
std::vector<std::string> unit_movement_states({"left", "right", "down", "up"});
for (size_t faction_idx = 0; faction_idx < unit_factions.size();
faction_idx++) {
// 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>>>());
unit_textures.push_back(std::vector<std::vector<std::pair<SDL_Texture*, int>>>());
for (size_t unit_idx = 0; unit_idx < units.size(); unit_idx++) {
// 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>>());
unit_textures.at(faction_idx).push_back(std::vector<std::pair<SDL_Texture*, int>>());
for (size_t state_idx = 0; state_idx < unit_states.size(); state_idx++) {
// 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 =
......@@ -182,9 +203,12 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
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++) {
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);
......@@ -193,19 +217,22 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
}
SDL_Texture* tmp = SDL_CreateTexture(
engine.renderer(), SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_STATIC, unit_frames.size() * 16, 16);
engine.renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC,
unit_frames.size() * 16, 16);
SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND);
if (tmp == nullptr) {
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) {
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()));
......@@ -216,11 +243,11 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
.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);
// 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);
......@@ -230,9 +257,12 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
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++) {
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);
......@@ -241,19 +271,22 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
}
SDL_Texture* tmp = SDL_CreateTexture(
engine.renderer(), SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_STATIC, unit_frames.size() * 24, 24);
engine.renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC,
unit_frames.size() * 24, 24);
SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND);
if (tmp == nullptr) {
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) {
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()));
......@@ -272,22 +305,27 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
this->unit_moving_height = 24;
// Effects
std::vector<std::string> effects({"land_explosion", "air_explosion",
"naval_explosion", "submarine_hide",
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]);
// 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);
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++) {
// 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);
......@@ -301,21 +339,20 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
SDL_SetTextureBlendMode(tmp, SDL_BLENDMODE_BLEND);
if (tmp == nullptr) {
if (tmp == nullptr)
{
throw std::runtime_error(
"Fehler beim Erstellen der Textur für die Effects: " +
std::string(SDL_GetError()));
"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) {
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()));
"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()));
effect_textures.push_back(std::pair<SDL_Texture*, int>(tmp, effect_frames.size()));
}
this->effect_width = 32;
......@@ -324,59 +361,98 @@ Spritesheet::Spritesheet(std::string path, Engine &engine) {
// 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() {
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() {
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() {
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() {
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) {
Spritesheet::~Spritesheet()
{
for (std::pair<SDL_Texture*, int> tile_texture : tile_textures)
{
SDL_DestroyTexture(tile_texture.first);
}
for (SDL_Texture *building_texture : building_textures) {
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) {
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.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 {
/**
* 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;
......@@ -22,36 +45,113 @@ public:
// 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();
std::vector<std::vector<std::vector<std::pair<SDL_Texture *, int>>>> &
get_unit_textures();
/**
* 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:
......@@ -66,8 +166,7 @@ private:
int building_height;
// Units
std::vector<std::vector<std::vector<std::pair<SDL_Texture *, int>>>>
unit_textures;
std::vector<std::vector<std::vector<std::pair<SDL_Texture*, int>>>> unit_textures;
int unit_width;
int unit_height;
int unit_moving_width;
......
......@@ -2,18 +2,17 @@
#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();
......@@ -28,8 +27,7 @@ void Tile::render(Engine &engine, int scale) {
dest.h = spritesheet->get_tile_height() * scale;
SDL_RenderCopyEx(
engine.renderer(),
spritesheet->get_tile_textures().at(static_cast<int>(id)).first, &src,
engine->renderer(), spritesheet->get_tile_textures().at(static_cast<int>(id)).first, &src,
&dest, 0, NULL, SDL_FLIP_NONE);
}
......
......@@ -3,9 +3,11 @@
#include "engine.hpp"
#include "scene.hpp"
namespace advanced_wars {
namespace advanced_wars
{
enum class TileId {
enum class TileId
{
PLAIN = 0,
WATER = 1,
FOREST = 2,
......@@ -38,14 +40,15 @@ enum class TileId {
CLIFF_INVERSE_CORNER_BOTTOM_RIGHT = 29,
};
class Tile {
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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment