aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Johnson <[email protected]>2025-05-07 00:08:29 -0400
committerSamuel Johnson <[email protected]>2025-05-07 00:08:29 -0400
commit5f383da62c7a80526634b68c28d7ba9d9e693e75 (patch)
tree7a8e1e8b6d4dcc07126f9e65ca2618aee0c920be
parent4223fb686b99a5db73da79fb73123568b75822ed (diff)
Create html shell wrapper for gameHEADtrunkdev
-rw-r--r--.gitignore10
-rw-r--r--CMakeLists.txt35
-rw-r--r--starless/main.cpp123
-rw-r--r--vcpkg.json17
-rw-r--r--wrapper.html91
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>
+