Cursed Tomorrow: a C++17 horror platformer built on a Raylib game core
A 2D horror-survival and escape game built from scratch in C++. Navigate unsettling levels, survive the glitches, and escape the nightmare. Features a custom C++ game core, robust asset management, and comprehensive system architecture documentation.
At a glance
- What is it?
- Cursed Tomorrow is a solo 2D horror survival game written from scratch in C++17 on top of Raylib, with a documented four module engine core and a Docker build that runs the test suite. It is worth reading as an engine architecture exercise, with the caveat that it is an alpha version with no published releases.
- Who is it for?
- CursedTomorrow suits developers who want a readable example of a decoupled C++17 game core, and the include/CursedTomorrow tree plus the architecture diagrams give that away more usefully than the game itself does. It does not suit anyone expecting a finished or stable game, since the version badge reads 1.0.0-alpha, the repository publishes no GitHub releases and the last push was on 2026-06-30.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A solo alpha built to demonstrate engine structure
Cursed Tomorrow is a 2D horror survival and escape game written from scratch in C++ with a custom game core, and it is presented openly as a technical demonstration of modular game design. The author describes it as a platforming descent into the atmospheric unknown, designed and engineered by Ahmad Hassan as a solo project, and the topics it carries include a custom game engine, Docker, CMake and software engineering. That framing matters for who the audience is. A player looking for a finished atmospheric horror title will find an alpha, a version badge of 1.0.0-alpha, and a repository whose last push was on 2026-06-30 with no GitHub releases published. A developer looking for a clean C++17 engine skeleton will find something more useful: a namespace, four modules with stated coupling levels, a unidirectional update phase and a documentation folder that includes a software requirements specification and a data structures presentation.
Four modules with declared coupling
The architecture section names four modules and gives each an explicit coupling rating, which is unusual and useful. Core owns the window lifecycle, the state machine and the orchestration of the main loop, and it is rated as high coupling because it acts as the orchestrator. Entities holds the individual game actors, their physics and their local state, and is rated low coupling with independent behavior. Resources provides centralized asset caching and memory safety and is rated at zero coupling as a utility. Config holds global constants and engine parameters and is static only. All interaction between them is managed strictly through the CursedTomorrow namespace, so nothing outside that namespace reaches into a module's internals. The main loop is drawn as a simple cycle: main.cpp initializes Core::Game, the game handles input, updates level logic, lets the character physics run, draws, and returns to the frame start.
The update phase runs one way only
The data flow inside a frame is described as a strict unidirectional pattern during the update phase, with the stated purpose of deterministic behavior. The sequence starts with the game calling HandleInput, then asking the character to update, which in turn runs Move and Jump, then ApplyGravity. Level logic updates after the character has moved, and the draw call follows. Texture loading is the one place where the flow reaches outside, because the character asks the ResourceManager to load a texture and receives the texture data back, after which the game reports the render as complete. Putting asset loading inside the character draw step is the notable design decision here, since it means the caching layer is touched during rendering rather than during a separate preloading phase. Determinism also depends on the physics living with the entity rather than in the engine, which is why the Entities module is rated low coupling even though gravity and jumping are applied every frame.
Building locally with CMake and the Raylib fetch
The build needs a C++17 compiler and CMake 3.15 or newer, and Raylib is the primary dependency, described as automatically fetched during the build process. The pipeline in the README is three commands:
# Configure with external Raylib fetch
cmake -B build -D FETCH_RAYLIB=ON
# Build the project
cmake --build build --config Release
# Run Unit Tests
ctest --test-dir buildThe FETCH_RAYLIB option is what makes the dependency automatic, and turning it off would require Raylib to be present on the system already. The third command is worth noticing: the unit tests are wired into CMake, so ctest is part of the normal build cycle rather than a separate manual step. The technical pipeline diagram shows the order the project intends for a change to travel through, linting, then CMake configuration, then the binary build, then unit testing, and finally a release artifact, and a GitHub Actions workflow is referenced in the badge row, so part of that chain runs in CI. The build output lands in a build directory, and the repository ships scripts/ for automation and build helpers alongside docs/ for the technical documentation.
The container image builds the project and then runs the tests
The repository ships both a Dockerfile and a docker-compose.yml, and the container is designed to verify the build rather than to launch the game. The base image is ubuntu:22.04 with build-essential, cmake, git and the X11 and audio development packages that Raylib expects on Linux, including libasound2-dev, libx11-dev, libxrandr-dev, libxi-dev, libgl1-mesa-dev, libglu1-mesa-dev, libxcursor-dev and libxinerama-dev. It then copies the project into /app, configures with the same FETCH_RAYLIB option used locally, builds in Release mode and, as its default command, runs ctest:
FROM ubuntu:22.04
# Avoid prompts from apt
ENV DEBIAN_FRONTEND=noninteractiveThe image then sets a working directory and copies the tree in:
# Set working directory
WORKDIR /app
# Copy the project
COPY . .and finishes by building the project and defaulting to the test command:
# Build the project
RUN cmake -B build -D FETCH_RAYLIB=ON \
&& cmake --build build --config Release
# Command to run tests (or build artifacts)
CMD ["ctest", "--test-dir", "build"]The compose file mounts the working tree into /app and runs the build command over it, so a container is a repeatable way to compile without polluting the host with a toolchain. The tradeoff is that a headless image with no display is fine for the test suite and useless for playing the game, so the container is a verification environment rather than a distribution channel. A desktop.ini file sits in the top level entries, which suggests the tree has been edited from Windows as well as Linux.
Screen and asset settings live in a dotenv file
Runtime defaults are kept out of the source and in a .env.example file, which the top level entries confirm is shipped. The values are plain and readable: a window of 1422 by 800 at a target of 60 frames per second, an assets directory and an audio directory both relative to the project root, and debug flags for debug mode and the FPS readout. These are the numbers the shipped window uses, so a change here is a change to the game's presentation rather than to a debug switch. The file also fixes the game title string as Cursed Tomorrow. The asset paths match the repository layout, where assets/ holds an audio folder described as atmospheric soundscapes and a mainScreen folder for visual interface components, and where screenshots of the main menu, gameplay, level select, warning and death screens live under docs/assets/screenshots. Nothing in the configuration exposes the graphics resolution scaling or the audio device, so the settings surface is small by design.
What a reader gets from the docs, and what is missing
The academic resources section points at a software requirements specification PDF covering requirements and data structures concepts, and a PPTX walkthrough of the concepts implemented in the game. Those two files are the more unusual part of the repository, because they are aimed at readers studying the code rather than players. Beyond them, the tree separates concerns in a conventional way: include/ holds headers under the CursedTomorrow directory with Core and Entities subfolders, src/ holds implementations, docs/ holds detailed technical documentation, and scripts/ holds automation. A CHANGELOG.md, ROADMAP.md, CONTRIBUTING.md, SECURITY.md and SUPPORT.md complete the top level. The gaps are the interesting part for an adopter. The README does not document how to run the game executable after a build, there is no platform list beyond the Linux container, and no releases are published, so a player cannot download a binary. The engine documentation is strong while the player documentation is thin, which is consistent with a project whose stated goal is intentional game engineering.
Editorial conclusion
CursedTomorrow suits developers who want a readable example of a decoupled C++17 game core, and the include/CursedTomorrow tree plus the architecture diagrams give that away more usefully than the game itself does. It does not suit anyone expecting a finished or stable game, since the version badge reads 1.0.0-alpha, the repository publishes no GitHub releases and the last push was on 2026-06-30. Before building it, check that your toolchain has a C++17 compiler and CMake 3.15 or newer, confirm the FETCH_RAYLIB option resolves in your environment or install Raylib yourself, and read the .env.example values such as SCREEN_WIDTH and TARGET_FPS, because those runtime defaults are what the shipped window uses.
Frequently asked questions
What is Cursed Tomorrow built with?
It is written in C++17 on top of Raylib, with a custom game core organised into Core, Entities, Resources and Config modules under the CursedTomorrow namespace.
How do I build Cursed Tomorrow from source?
Configure with cmake -B build -D FETCH_RAYLIB=ON, then run cmake --build build --config Release, and use ctest --test-dir build to run the unit tests. A C++17 compiler and CMake 3.15 or newer are required.
Is there a Docker build for Cursed Tomorrow?
Yes. The Dockerfile is based on ubuntu:22.04, installs the build tools and Raylib system packages, builds the project and defaults to running ctest, and docker-compose.yml mounts the working tree into /app.
Can I change the game window size and frame rate?
The .env.example file sets SCREEN_WIDTH=1422, SCREEN_HEIGHT=800 and TARGET_FPS=60, along with asset paths and debug flags such as DEBUG_MODE and SHOW_FPS.
Is Cursed Tomorrow a finished game?
No. The version badge reads 1.0.0-alpha, the repository publishes no GitHub releases, and the last push was on 2026-06-30.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ahmadhassan-bted-cursedtomorrow)Was this useful?