diff options
author | Samuel Johnson <[email protected]> | 2025-05-07 00:08:29 -0400 |
---|---|---|
committer | Samuel Johnson <[email protected]> | 2025-05-07 00:08:29 -0400 |
commit | 5f383da62c7a80526634b68c28d7ba9d9e693e75 (patch) | |
tree | 7a8e1e8b6d4dcc07126f9e65ca2618aee0c920be | |
parent | 4223fb686b99a5db73da79fb73123568b75822ed (diff) |
-rw-r--r-- | .gitignore | 10 | ||||
-rw-r--r-- | CMakeLists.txt | 35 | ||||
-rw-r--r-- | starless/main.cpp | 123 | ||||
-rw-r--r-- | vcpkg.json | 17 | ||||
-rw-r--r-- | wrapper.html | 91 |
5 files changed, 276 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5de5580 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +assets/ +build/ +.cache/ + +*.exe +*.out +*.d +*.o + +*.swp diff --git a/CMakeLists.txt b/CMakeLists.txt index e69de29..a460e57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.19...4.0) + +option(BUILD_NATIVE "Compile for host toolchain instead of web" OFF) +if (NOT BUILD_NATIVE) + message("Compiling with emscripten...") + set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "$ENV{EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake") + set(VCPKG_TARGET_TRIPLET "wasm32-emscripten") +endif() + +set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vendor/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain") + +project(starless VERSION 0.1 LANGUAGES C CXX) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") + +file( + GLOB_RECURSE SOURCE_FILES + CONFIGURE_DEPENDS + ${PROJECT_SOURCE_DIR}/starless/*.cpp +) + +add_executable(${PROJECT_NAME} ${SOURCE_FILES}) +target_include_directories(${PROJECT_NAME} PRIVATE .) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) + +if (NOT BUILD_NATIVE) + target_link_options(${PROJECT_NAME} PRIVATE --preload-file "${CMAKE_CURRENT_SOURCE_DIR}/data@data" --shell-file "${CMAKE_CURRENT_SOURCE_DIR}/wrapper.html") + set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".html") +endif() + +find_package(SDL3 CONFIG REQUIRED) +find_package(Lua CONFIG REQUIRED) +find_package(libtcod CONFIG REQUIRED) +target_link_libraries(${PROJECT_NAME} PRIVATE SDL3::SDL3 libtcod::libtcod ${LUA_LIBRARIES}) diff --git a/starless/main.cpp b/starless/main.cpp new file mode 100644 index 0000000..458b521 --- /dev/null +++ b/starless/main.cpp @@ -0,0 +1,123 @@ +#define SDL_MAIN_USE_CALLBACKS +#include <SDL3/SDL.h> +#include <SDL3/SDL_main.h> + +#include <cstdlib> +#include <filesystem> +#include <iostream> +#include <libtcod.hpp> + +/// Return the data directory. +auto get_data_dir() -> std::filesystem::path { + static auto root_directory = std::filesystem::path{"."}; // Begin at the working directory. + while (!std::filesystem::exists(root_directory / "data")) { + // If the current working directory is missing the data dir then it will assume it exists in any parent directory. + root_directory /= ".."; + if (!std::filesystem::exists(root_directory)) { + throw std::runtime_error("Could not find the data directory."); + } + } + return root_directory / "data"; +}; + +static constexpr auto WHITE = tcod::ColorRGB{255, 255, 255}; + +static tcod::Console g_console; // The global console object. +static tcod::Context g_context; // The global libtcod context. + +static int player_x{}; +static int player_y{}; + +// Called every frame +SDL_AppResult SDL_AppIterate(void*) { + g_console.clear(); + if (g_console.in_bounds({player_x, player_y})) { + g_console.at({player_x, player_y}).ch = '@'; + } + g_context.present(g_console); + return SDL_APP_CONTINUE; +} +// Handle events +SDL_AppResult SDL_AppEvent(void*, SDL_Event* event) { + switch (event->type) { + case SDL_EVENT_KEY_DOWN: + switch (event->key.key) { + case SDLK_LEFT: // Arrow key + case SDLK_H: // Vi key + case SDLK_KP_4: // Keypad + player_x -= 1; + break; + case SDLK_RIGHT: + case SDLK_L: + case SDLK_KP_6: + player_x += 1; + break; + case SDLK_UP: + case SDLK_K: + case SDLK_KP_8: + player_y -= 1; + break; + case SDLK_DOWN: + case SDLK_J: + case SDLK_KP_2: + player_y += 1; + break; + case SDLK_HOME: + case SDLK_Y: + case SDLK_KP_7: + player_x -= 1; + player_y -= 1; + break; + case SDLK_PAGEUP: + case SDLK_U: + case SDLK_KP_9: + player_x += 1; + player_y -= 1; + break; + case SDLK_END: + case SDLK_B: + case SDLK_KP_1: + player_x -= 1; + player_y += 1; + break; + case SDLK_PAGEDOWN: + case SDLK_N: + case SDLK_KP_3: + player_x += 1; + player_y += 1; + break; + case SDLK_PERIOD: + case SDLK_CLEAR: + case SDLK_KP_5: + break; + } + break; + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; + } + return SDL_APP_CONTINUE; +} +// Main entry point +SDL_AppResult SDL_AppInit(void**, int argc, char** argv) { + auto params = TCOD_ContextParams{}; + params.argc = argc; + params.argv = argv; + params.renderer_type = TCOD_RENDERER_SDL2; + params.vsync = 1; + params.sdl_window_flags = SDL_WINDOW_RESIZABLE; + params.window_title = "This Starless Sky - Paterissa"; + + auto tileset = tcod::load_tilesheet(get_data_dir() / "assets/dejavu16x16_gs_tc.png", {32, 8}, tcod::CHARMAP_TCOD); + params.tileset = tileset.get(); + + g_console = tcod::Console{80, 40}; + player_x = g_console.get_width() / 2; + player_y = g_console.get_height() / 2; + params.console = g_console.get(); + + g_context = tcod::Context(params); + + return SDL_APP_CONTINUE; +} +// Called before existing +void SDL_AppQuit(void*, SDL_AppResult) {} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..a5b8de9 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", + "dependencies": [ + { + "name": "libtcod", + "default-features": true, + "features": [ + "png", + "sdl", + "unicode", + "zlib" + ] + }, + "sdl3", + "lua" + ] +} diff --git a/wrapper.html b/wrapper.html new file mode 100644 index 0000000..0bd537b --- /dev/null +++ b/wrapper.html @@ -0,0 +1,91 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <style> + body { + color: white; + background-color: black; + } + + header { + text-align: center; + } + + #loading_bg { + position: absolute; + left: 25%; + top: 50%; + + width: 50%; + background-color: grey; + } + + #loading_bar { + width: 0%; + padding: 2px; + background-color: red; + } + + .game_canvas { + display: none; + margin: auto; + outline-style: double; + outline-color: red; + } + </style> +</head> +<body> + <header> + <h1>This Starless Sky</h1> + <p>we are deserving of no gifts.</p? + </header> + <main> + <div id="loading_bg"> + <div id="loading_bar"></div> + </div> + <canvas class="game_canvas" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas> + </main> + <script> + let progressBar = document.getElementById("loading_bar"); + let canvas = document.getElementById("canvas"); + + function convertStatusToNumeric (status) { + if (status === "Running...") { + return 100; + } else if (status.length === 0) { + return 0; + } + + // Ungodly regex from the depths of hell, see + // https://github.com/emscripten-core/emscripten/issues/16278#issuecomment-2178303577 + const regExp = RegExp(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); + const matches = regExp.exec(status); + + if (matches) { + const currentValue = parseInt(matches[2]); + const maxValue = parseInt(matches[4]); + return (currentValue * 100) / maxValue; + } else { + return NaN; + } + } + + var Module = { + setStatus (status) { + if (progressBar.style.width !== "100%") { + const percentage = convertStatusToNumeric(status); + progressBar.style.width = percentage + "%"; + } else { + progressBar.style.display = "none"; + canvas.style.display = "block"; + } + }, + + }; + </script> + {{{ SCRIPT }}} +</body> +</html> + |