diff --git a/CMakeLists.txt b/CMakeLists.txt index ccc7b3e..ca20ba0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,12 +151,28 @@ else() DEPENDS "${filename}" ) endmacro() + macro(copy_dir filename) + if (ANDROID) + # MOBILE_ASSETS_DIR is set in the gradle file via the cmake command line and points to the Android Studio Assets folder. + # when we copy assets there, the Android build pipeline knows to add them to the apk. + set(outname "${MOBILE_ASSETS_DIR}/${filename}") + else() + # for platforms that do not have a good packaging format, all we can do is copy the assets to the process working directory. + set(outname "${CMAKE_BINARY_DIR}/$/${filename}") + endif() + add_custom_command(POST_BUILD + TARGET "${EXECUTABLE_NAME}" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_LIST_DIR}/src/${filename}" "${outname}" + DEPENDS "${filename}" + ) + endmacro() copy_helper("assets/Inter-VariableFont.ttf") copy_helper("assets/MartianMono-VariableFont.ttf") copy_helper("assets/sunset_on_the_beach.ogg") copy_helper("assets/success.ogg") copy_helper("assets/failure.ogg") copy_helper("assets/gs_tiger.svg") + copy_dir("assets/textures/") endif() # set some extra configs for each platform diff --git a/src/adventura.cpp b/src/adventura.cpp index be07f14..893f99f 100644 --- a/src/adventura.cpp +++ b/src/adventura.cpp @@ -59,7 +59,7 @@ bool startWorld(string worldFile) { world.loadFromFile(worldFile); Player player = Player(world.getStartPos(), world); - render(world, player.mapToWorldspace()); + renderWorld(world); inputLoop(player, world, testMode, worldIndex); diff --git a/src/appcontext.hpp b/src/appcontext.hpp new file mode 100644 index 0000000..a5fad70 --- /dev/null +++ b/src/appcontext.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "player.hpp" +#include "world.hpp" +#include "camera.hpp" + +struct AppContext { + SDL_Window* window; + SDL_Renderer* renderer; + SDL_Texture* messageTex, *imageTex; + SDL_FRect messageDest; + SDL_AudioDeviceID audioDevice; + Mix_Music* music; + SDL_AppResult app_quit = SDL_APP_CONTINUE; + Player* player; + World* world; + TTF_Font* font; + std::filesystem::path basePath; + Pos camera; +}; \ No newline at end of file diff --git a/src/assets/textures/box.png b/src/assets/textures/box.png new file mode 100644 index 0000000..6d28df3 Binary files /dev/null and b/src/assets/textures/box.png differ diff --git a/src/assets/textures/ladder.png b/src/assets/textures/ladder.png new file mode 100644 index 0000000..f6398db Binary files /dev/null and b/src/assets/textures/ladder.png differ diff --git a/src/assets/textures/platform.png b/src/assets/textures/platform.png new file mode 100755 index 0000000..496d17a Binary files /dev/null and b/src/assets/textures/platform.png differ diff --git a/src/assets/textures/player_falling.png b/src/assets/textures/player_falling.png new file mode 100644 index 0000000..95d067a Binary files /dev/null and b/src/assets/textures/player_falling.png differ diff --git a/src/assets/textures/player_idle.png b/src/assets/textures/player_idle.png new file mode 100644 index 0000000..429e55b Binary files /dev/null and b/src/assets/textures/player_idle.png differ diff --git a/src/assets/textures/sand.png b/src/assets/textures/sand.png new file mode 100644 index 0000000..9cb54a8 Binary files /dev/null and b/src/assets/textures/sand.png differ diff --git a/src/assets/textures/spike.png b/src/assets/textures/spike.png new file mode 100644 index 0000000..b255ec5 Binary files /dev/null and b/src/assets/textures/spike.png differ diff --git a/src/assets/textures/wall.png b/src/assets/textures/wall.png new file mode 100644 index 0000000..09f850c Binary files /dev/null and b/src/assets/textures/wall.png differ diff --git a/src/assets/textures/water.png b/src/assets/textures/water.png new file mode 100644 index 0000000..86e3f3e Binary files /dev/null and b/src/assets/textures/water.png differ diff --git a/src/assets/textures_base/box.pxo b/src/assets/textures_base/box.pxo new file mode 100644 index 0000000..ae24906 Binary files /dev/null and b/src/assets/textures_base/box.pxo differ diff --git a/src/assets/textures_base/ladder.pxo b/src/assets/textures_base/ladder.pxo new file mode 100644 index 0000000..52e8d50 Binary files /dev/null and b/src/assets/textures_base/ladder.pxo differ diff --git a/src/assets/textures_base/platform.pxo b/src/assets/textures_base/platform.pxo new file mode 100644 index 0000000..7106b6d Binary files /dev/null and b/src/assets/textures_base/platform.pxo differ diff --git a/src/assets/textures_base/player_falling.pxo b/src/assets/textures_base/player_falling.pxo new file mode 100644 index 0000000..b567a77 Binary files /dev/null and b/src/assets/textures_base/player_falling.pxo differ diff --git a/src/assets/textures_base/player_idle.pxo b/src/assets/textures_base/player_idle.pxo new file mode 100644 index 0000000..bc85601 Binary files /dev/null and b/src/assets/textures_base/player_idle.pxo differ diff --git a/src/assets/textures_base/sand.pxo b/src/assets/textures_base/sand.pxo new file mode 100644 index 0000000..b9d420a Binary files /dev/null and b/src/assets/textures_base/sand.pxo differ diff --git a/src/assets/textures_base/spike.pxo b/src/assets/textures_base/spike.pxo new file mode 100644 index 0000000..b826101 Binary files /dev/null and b/src/assets/textures_base/spike.pxo differ diff --git a/src/assets/textures_base/wall.pxo b/src/assets/textures_base/wall.pxo new file mode 100644 index 0000000..b95dd5f Binary files /dev/null and b/src/assets/textures_base/wall.pxo differ diff --git a/src/assets/textures_base/water.pxo b/src/assets/textures_base/water.pxo new file mode 100644 index 0000000..efbd4f0 Binary files /dev/null and b/src/assets/textures_base/water.pxo differ diff --git a/src/camera.hpp b/src/camera.hpp new file mode 100644 index 0000000..0f154df --- /dev/null +++ b/src/camera.hpp @@ -0,0 +1,3 @@ +struct Pos { + float x, y; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ebefc7c..f118e7d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,11 +7,15 @@ #include #include #include +#include #include "adventura.cpp" #include "events.hpp" +#include "renderer.cpp" +#include "appcontext.hpp" using std::pair; +using std::map; constexpr uint32_t windowStartWidth = 1200; constexpr uint32_t windowStartHeight = 800; @@ -20,20 +24,7 @@ void loadWorld(void* appstate, string worldFile); void resetWorld(void* appstate); void loadNextWorld(void* appstate); void playSound(void* appstate, string soundFile); - -struct AppContext { - SDL_Window* window; - SDL_Renderer* renderer; - SDL_Texture* messageTex, *imageTex; - SDL_FRect messageDest; - SDL_AudioDeviceID audioDevice; - Mix_Music* music; - SDL_AppResult app_quit = SDL_APP_CONTINUE; - Player* player; - World* world; - TTF_Font* font; - std::filesystem::path basePath; -}; +void setScale(void* appstate); SDL_AppResult SDL_Fail(){ SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Error %s", SDL_GetError()); @@ -160,11 +151,11 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { loadNextWorld(*appstate); SDL_SetRenderVSync(renderer, -1); // enable vysnc + + setScale(*appstate); SDL_Log("Application started successfully!"); - //start(argc, argv); - return SDL_APP_CONTINUE; } @@ -191,6 +182,10 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event* event) { playSound(appstate, "assets/failure.ogg"); resetWorld(appstate); } + + if (event->type == SDL_EVENT_WINDOW_RESIZED) { + setScale(appstate); + } if (event->type == SDL_EVENT_QUIT) { app->app_quit = SDL_APP_SUCCESS; @@ -198,7 +193,7 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event* event) { return SDL_APP_CONTINUE; } -vector, SDL_Texture*>> textureCache; + SDL_AppResult SDL_AppIterate(void *appstate) { auto* app = (AppContext*)appstate; @@ -210,44 +205,16 @@ SDL_AppResult SDL_AppIterate(void *appstate) { auto blue = (std::sin(time) * 2 + 1) / 2.0 * 255; SDL_SetRenderDrawBlendMode(app->renderer, SDL_BLENDMODE_ADD); SDL_SetRenderDrawColor(app->renderer, red, green, blue, 1); - SDL_SetRenderDrawColor(app->renderer, 0, 0, 0, 20); + SDL_SetRenderDrawColor(app->renderer, 20, 20, 20, 20); SDL_RenderClear(app->renderer); + setCameraPos(appstate); + // Renderer uses the painter's algorithm to make the text appear above the image, we must render the image first. //SDL_RenderTexture(app->renderer, app->imageTex, NULL, NULL); - vector> worldState = render(*(app->world), (*app->player).mapToWorldspace()); - for (int i = 0; i < worldState.size(); i++) { - vector line = worldState.at(i); - SDL_Texture* messageTex; - if (i < textureCache.size() && textureCache.at(i).first == line) { - messageTex = textureCache.at(i).second; - } - else { - if (i < textureCache.size()) { - SDL_DestroyTexture(textureCache.at(i).second); - } - SDL_Surface* surfaceMessage = TTF_RenderText_Solid(app->font, line.data(), line.size(), { 255,255,255 }); - - // make a texture from the surface - messageTex = SDL_CreateTextureFromSurface(app->renderer, surfaceMessage); - - SDL_DestroySurface(surfaceMessage); - while (textureCache.size() <= i) { - textureCache.push_back({{}, nullptr}); - } - textureCache.at(i) = {line, messageTex}; - } - auto messageTexProps = SDL_GetTextureProperties(messageTex); - SDL_FRect text_rect{ - .x = 0, - .y = float(i * SDL_GetNumberProperty(messageTexProps, SDL_PROP_TEXTURE_HEIGHT_NUMBER, 0)*2), - .w = float(SDL_GetNumberProperty(messageTexProps, SDL_PROP_TEXTURE_WIDTH_NUMBER, 0))*2, - .h = float(SDL_GetNumberProperty(messageTexProps, SDL_PROP_TEXTURE_HEIGHT_NUMBER, 0))*2 - }; - - SDL_RenderTexture(app->renderer, messageTex, NULL, &text_rect); - } + renderWorld(appstate); + renderPlayer(appstate); //SDL_RenderTexture(app->renderer, app->messageTex, NULL, &app->messageDest); SDL_RenderPresent(app->renderer); @@ -302,6 +269,7 @@ void loadNextWorld(void* appstate) { int index = std::find(worldFiles.begin(), worldFiles.end(), currentWorld) - worldFiles.begin(); if (index + 1 < worldFiles.size()) { loadWorld(appstate, worldFiles.at(index + 1)); + enteredNextWorld = true; } else { resetWorld(appstate); diff --git a/src/output.hpp b/src/output.hpp index 949c4f4..c68f367 100644 --- a/src/output.hpp +++ b/src/output.hpp @@ -9,6 +9,28 @@ static void jumpBackOneLine() { } +/** + * Renders the current state of the game world and player onto the console. + * It prints the world's blocks with their respective colors and encodings (characters). + * + * @param world Reference to the World object representing the current world. + */ +static vector> renderWorld(World &world) { + vector> canvas = world.getFieldState(); + vector> out; + + for (unsigned int y = 0; y <= world.getMaxY(); y++) { + vector line; + for (unsigned int x = 0; x <= world.getMaxX(); x++) { + if (canvas.size() > y && canvas.at(y).size() > x) { + line.push_back(canvas.at(y).at(x).getEncoding()); + } + else line.push_back(' '); + } + out.push_back(line); + } + return out; +} /** * Renders the current state of the game world and player onto the console. * It prints the world's blocks with their respective colors and encodings (characters). @@ -17,7 +39,7 @@ static void jumpBackOneLine() { * @param world Reference to the World object representing the current world. * @param playerTexture Reference to the current Player texture. */ -static vector> render(World &world, vector> playerTexture) { +static vector> renderPlayer(World &world, vector> playerTexture) { vector> canvas = world.getFieldState(); vector> out; @@ -29,10 +51,6 @@ static vector> render(World &world, vector> playerText line.push_back(playerTexture.at(y).at(x)); //cout << Color::BRIGHT_YELLOW << playerTexture.at(y).at(x); } - else if (canvas.size() > y && canvas.at(y).size() > x) { - line.push_back(canvas.at(y).at(x).getEncoding()); - //cout << canvas.at(y).at(x).getColor() << canvas.at(y).at(x).getEncoding(); - } else line.push_back(' '); } out.push_back(line); diff --git a/src/renderer.cpp b/src/renderer.cpp new file mode 100644 index 0000000..e3d18b8 --- /dev/null +++ b/src/renderer.cpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "appcontext.hpp" + +using std::pair; +using std::map; + +float scale = 0; +void setScale(void* appstate) { + auto* app = (AppContext*)appstate; + int width, height, bbwidth, bbheight; + SDL_GetWindowSize(app->window, &width, &height); + SDL_GetWindowSizeInPixels(app->window, &bbwidth, &bbheight); + scale = float(width) / 500; + SDL_Log("Scale: %f", scale); +} + +map> textureCache; +void renderWorld(void* appstate) { + auto* app = (AppContext*)appstate; + + vector> worldState = renderWorld(*(app->world)); + for (int y = 0; y < worldState.size(); y++) { + vector line = worldState.at(y); + for (int x = 0; x < line.size(); x++) { + char blockChar = line.at(x); + SDL_Texture* messageTex; + if (textureCache.contains(blockChar)) { + messageTex = textureCache.at(blockChar).first; + } + else { + SDL_Surface* surface; + Block block = (*app->world).getBlockRegistry().getByEncoding(blockChar); + string texturePath = (app->basePath / "assets/textures/").string() + block.getId().path_ + ".png"; + bool isText = true; + if (std::filesystem::exists(texturePath)) { + isText = false; + // load the SVG + surface = IMG_Load(texturePath.c_str()); + } + else { + std::string_view block_str = string(1, blockChar); + surface = TTF_RenderText_Solid(app->font, block_str.data(), block_str.size(), { 255,255,255 }); + } + // make a texture from the surface + messageTex = SDL_CreateTextureFromSurface(app->renderer, surface); + SDL_SetTextureScaleMode(messageTex, SDL_ScaleMode::SDL_SCALEMODE_NEAREST); + + SDL_DestroySurface(surface); + textureCache.insert({blockChar, {messageTex, isText}}); + } + auto messageTexProps = SDL_GetTextureProperties(messageTex); + SDL_FRect text_rect{ + .x = float(x * 16 * scale)+3 + app->camera.x, + .y = float(y * 16 * scale) + app->camera.y, + .w = float(10 * scale), + .h = float(16 * scale) + }; + SDL_FRect texture_rect{ + .x = float(x * 16 * scale) + app->camera.x, + .y = float(y * 16 * scale) + app->camera.y, + .w = float(16 * scale), + .h = float(16 * scale) + }; + bool isText = textureCache.at(blockChar).second; + + SDL_RenderTexture(app->renderer, messageTex, NULL, isText ? &text_rect : &texture_rect); + } + } +} +map playerTextureCache; +void renderPlayer(void* appstate) { + auto* app = (AppContext*)appstate; + BlockPos playerPos = (*app->player).getPos(); + + string state = "idle"; + if ((*app->player).isFreeFalling()) state = "falling"; + if (!(*app->player).isAlive()) state = "dead"; + SDL_Texture* messageTex; + if (playerTextureCache.contains(state)) { + messageTex = playerTextureCache.at(state); + } + else { + SDL_Surface* surface; + string texturePath = (app->basePath / "assets/textures/").string() + "player_"+ state + ".png"; + if (std::filesystem::exists(texturePath)) { + // load the SVG + surface = IMG_Load(texturePath.c_str()); + } + // make a texture from the surface + messageTex = SDL_CreateTextureFromSurface(app->renderer, surface); + SDL_SetTextureScaleMode(messageTex, SDL_ScaleMode::SDL_SCALEMODE_NEAREST); + + SDL_DestroySurface(surface); + playerTextureCache.insert({state, messageTex}); + } + auto messageTexProps = SDL_GetTextureProperties(messageTex); + SDL_FRect text_rect{ + .x = float((playerPos.getX()-0.5) * 16 * scale) + app->camera.x, + .y = float(playerPos.getY() * 16 * scale) + app->camera.y, + .w = float(32 * scale), + .h = float(32 * scale) + }; + + SDL_RenderTexture(app->renderer, messageTex, NULL, &text_rect); +} + +bool enteredNextWorld = false; +auto smoothStartTime = 0; +int targetCameraX = 0; +void setCameraPos(void* appstate) { + auto* app = (AppContext*)appstate; + auto time = SDL_GetTicks() / 100000.f; + + int lastCameraX = app->camera.x; + targetCameraX = -(std::max(0, app->player->getPos().getX() - 5)) * 16 * scale; + if (!enteredNextWorld) { + if (lastCameraX != app->camera.x) smoothStartTime = time; + app->camera.x = lastCameraX + (targetCameraX - lastCameraX) * std::min(1.0f, (time - smoothStartTime) * 0.25f); + } + else { + app->camera.x = targetCameraX; + enteredNextWorld = false; + } +} \ No newline at end of file diff --git a/worlds/4.txt b/worlds/4.txt index 6735422..ee8ac62 100644 --- a/worlds/4.txt +++ b/worlds/4.txt @@ -3,7 +3,7 @@ Welt 4: In der Klemme S - --- + -0- 0 0 x x 0 ------------- diff --git a/worlds/5.txt b/worlds/5.txt index 66c9a6b..a4f34a1 100644 --- a/worlds/5.txt +++ b/worlds/5.txt @@ -4,7 +4,7 @@ Welt 5: Die Beachparty 0***************0 0 0 x -------^^^^^^^^^^^^^^^^^^^------------ - H ---------------- + H -0-------------- H H H 0 H S H 0 H