summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt24
-rw-r--r--src/bitmap.cpp53
-rw-r--r--src/bitmap.h22
-rw-r--r--src/level.cpp54
-rw-r--r--src/level.h28
-rw-r--r--src/main.cpp154
-rw-r--r--src/train.cpp52
-rw-r--r--src/train.h27
-rw-r--r--src/window.cpp61
-rw-r--r--src/window.h36
10 files changed, 511 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..7e71ff2
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,24 @@
+SET(SOURCES
+ bitmap.cpp
+ level.cpp
+ main.cpp
+ train.cpp
+ window.cpp
+)
+
+add_executable(
+ Nonortho
+ ${SOURCES}
+)
+
+target_include_directories(
+ Nonortho
+ PUBLIC
+ ${PROJECT_SOURCE_DIR}/include
+)
+
+target_link_libraries(
+ Nonortho
+ PRIVATE
+ SDL2::SDL2
+)
diff --git a/src/bitmap.cpp b/src/bitmap.cpp
new file mode 100644
index 0000000..612f2af
--- /dev/null
+++ b/src/bitmap.cpp
@@ -0,0 +1,53 @@
+#include "bitmap.h"
+
+#define STB_IMAGE_IMPLEMENTATION
+#include <stb/stb_image.h>
+
+Bitmap::Bitmap(const char* image)
+{
+ int width, height, bitDepth;
+
+ uint8_t* buffer = stbi_load(image, &width, &height, &bitDepth, STBI_rgb_alpha);
+ if (!buffer) {
+ return;
+ }
+
+ this->width = width;
+ this->height = height;
+ data = new uint32_t[width * height];
+
+ for (int i = 0; i < width * height; i++) {
+ uint8_t r = buffer[i * 4 + 0];
+ uint8_t g = buffer[i * 4 + 1];
+ uint8_t b = buffer[i * 4 + 2];
+ uint8_t a = buffer[i * 4 + 3];
+
+ data[i] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+
+ stbi_image_free(buffer);
+}
+
+void Bitmap::clear(uint32_t colour)
+{
+ memset(data, colour, width * height * 4);
+}
+
+void Bitmap::blit(Bitmap const& other, int xo, int yo, int xc, int yc, int w, int h)
+{
+ constexpr int MaskColour = 0xffff00ff;
+
+ for (int y = 0; y < h; ++y) {
+ int yp = y + yo;
+ if (yp < 0 || yp >= height) continue;
+
+ for (int x = 0; x < w; ++x) {
+ int xp = x + xo;
+ if (xp < 0 || xp >= width) continue;
+
+ int src = other.data[(x + xc) + (y + yc) * other.width];
+ if (src != MaskColour)
+ data[xp + yp * width] = src;
+ }
+ }
+}
diff --git a/src/bitmap.h b/src/bitmap.h
new file mode 100644
index 0000000..76f2f7a
--- /dev/null
+++ b/src/bitmap.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <cstdint>
+
+class Bitmap {
+public:
+ Bitmap(int width, int height)
+ : width(width), height(height)
+ {
+ data = new uint32_t[width * height];
+ }
+
+ Bitmap(const char* image);
+
+ ~Bitmap() { delete[] data; }
+
+ void clear(uint32_t colour);
+ void blit(Bitmap const& other, int x, int y, int xc, int yc, int w, int h);
+
+ uint32_t* data;
+ int width, height;
+}; \ No newline at end of file
diff --git a/src/level.cpp b/src/level.cpp
new file mode 100644
index 0000000..4df1d5a
--- /dev/null
+++ b/src/level.cpp
@@ -0,0 +1,54 @@
+#include "level.h"
+
+Level::Level(int width, int height)
+ : m_width(width), m_height(height)
+{
+ m_tiles = new uint8_t[width * height];
+ memset(m_tiles, 0, width * height);
+}
+
+uint8_t Level::get(int x, int y)
+{
+ if (inBounds(x, y)) {
+ return m_tiles[x + y * m_width];
+ } else {
+ return 0;
+ }
+}
+
+void Level::set(int x, int y, uint8_t tile)
+{
+ if (inBounds(x, y))
+ m_tiles[x + y * m_width] = tile;
+}
+
+RailDirection ChooseDirection(Level& level, int x, int y)
+{
+ if (!level.inBounds(x, y)) return NorthSouth;
+
+ bool n = level.get(x, y - 1) > 0;
+ bool e = level.get(x + 1, y) > 0;
+ bool s = level.get(x, y + 1) > 0;
+ bool w = level.get(x - 1, y) > 0;
+
+ if ((n || s) && !(e || w)) {
+ return NorthSouth;
+ }
+ if ((e || w) && !(n || s)) {
+ return EastWest;
+ }
+ else if (s && e) {
+ return SouthEast;
+ }
+ else if (s && w) {
+ return SouthWest;
+ }
+ else if (n && w) {
+ return NorthWest;
+ }
+ else if (n && e) {
+ return NorthEast;
+ }
+
+ return NorthSouth;
+} \ No newline at end of file
diff --git a/src/level.h b/src/level.h
new file mode 100644
index 0000000..0ffdac2
--- /dev/null
+++ b/src/level.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <cstdint>
+#include <cstring>
+
+class Level {
+public:
+ Level(int width, int height);
+ ~Level() { delete[] m_tiles; }
+
+ uint8_t get(int x, int y);
+ void set(int x, int y, uint8_t tile);
+ bool inBounds(int x, int y) { return x >= 0 && x < m_width && y >= 0 && y < m_height; }
+private:
+ int m_width, m_height;
+ uint8_t* m_tiles;
+};
+
+enum RailDirection {
+ NorthSouth = 1,
+ EastWest,
+ SouthEast,
+ SouthWest,
+ NorthWest,
+ NorthEast,
+};
+
+RailDirection ChooseDirection(Level& level, int x, int y); \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..b1942c9
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,154 @@
+#define SDL_MAIN_HANDLED
+#include <cmath>
+
+#include "window.h"
+#include "bitmap.h"
+#include "level.h"
+#include "train.h"
+
+struct Point2D {
+ float x, y;
+};
+
+Point2D screenToTile(Point2D source)
+{
+ Point2D result;
+
+ constexpr int TILE_SIZE = 24;
+
+ float mx = source.x;
+ float my = source.y;
+ float xx = (mx / TILE_SIZE + my / (TILE_SIZE / 2.f));
+ float yy = (my / (TILE_SIZE / 2.f) - (mx / TILE_SIZE));
+
+ result.x = floor(xx - 1.5f); // Magic numbers
+ result.y = floor(yy - 0.5f);
+
+ return result;
+}
+
+void DrawLevel(Bitmap& bitmap, Bitmap& tiles, Level& level, int xo, int yo)
+{
+ bitmap.clear(0xff224466);
+
+ for (int y = 0; y < 32; ++y) {
+ for (int x = 0; x < 32; ++x) {
+ constexpr int TILE_SIZE = 24;
+
+ auto tile = level.get(x, y);
+
+ int xx = (x - y) * (TILE_SIZE / 2) - xo;
+ int yy = (x + y) * (TILE_SIZE / 4) - yo;
+
+ int tx = (x + y) % 2;
+ int ty = 0;
+
+ if (tile == NorthSouth) {
+ tx = 0;
+ ty = 2;
+ }
+ else if (tile == EastWest) {
+ tx = 1;
+ ty = 2;
+ }
+ else if (tile == SouthEast) {
+ tx = 0;
+ ty = 3;
+ }
+ else if (tile == SouthWest) {
+ tx = 3;
+ ty = 3;
+ }
+ else if (tile == NorthWest) {
+ tx = 1;
+ ty = 3;
+ }
+ else if (tile == NorthEast) {
+ tx = 2;
+ ty = 3;
+ }
+
+ bitmap.blit(tiles, xx, yy, tx * 24, ty * 24, TILE_SIZE, TILE_SIZE);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ constexpr int Height = 240;
+ constexpr int Width = Height * 16 / 9;
+ constexpr int Scale = 3;
+
+ Window window("Nonortho", Width, Height, Scale);
+
+ Bitmap tiles("../res/tiles.png");
+ Level level(32, 32);
+
+ Bitmap bitmap(Width, Height);
+ TrainCar car(0, 0);
+
+ int xOffs = 0, yOffs = 0;
+ int xDrag, yDrag;
+ bool isDragging = false;
+
+ window.onMouseDown([&](int button, int x, int y) {
+ if (button == 1) {
+ static const auto update_direction = [&](int xt, int yt) {
+ if (level.get(xt, yt) > 0)
+ level.set(xt, yt, ChooseDirection(level, xt, yt));
+ };
+
+ float mx = x / Scale + xOffs;
+ float my = y / Scale + yOffs;
+ Point2D tilePos = screenToTile({ mx, my });
+
+ if (level.inBounds(tilePos.x, tilePos.y)) {
+ auto tile = level.get(tilePos.x, tilePos.y);
+
+ if (tile > 0) tile = 0;
+ else tile = ChooseDirection(level, tilePos.x, tilePos.y);
+
+ level.set(tilePos.x, tilePos.y, tile);
+
+ update_direction(tilePos.x - 1, tilePos.y);
+ update_direction(tilePos.x + 1, tilePos.y);
+ update_direction(tilePos.x, tilePos.y - 1);
+ update_direction(tilePos.x, tilePos.y + 1);
+ }
+ } else if (button == 2 && !isDragging) {
+ xDrag = x;
+ yDrag = y;
+ isDragging = true;
+ } else {
+ float mx = x / Scale + xOffs;
+ float my = y / Scale + yOffs;
+ auto pos = screenToTile({ mx, my });
+ car.x = pos.x;
+ car.y = pos.y;
+ }
+ });
+
+ window.onMouseUp([&](int button, int x, int y) {
+ if (button == 2 && isDragging)
+ isDragging = false;
+ });
+
+ window.onMouseMove([&](int x, int y) {
+ if (isDragging) {
+ xOffs -= (x - xDrag) / Scale;
+ yOffs -= (y - yDrag) / Scale;
+ xDrag = x;
+ yDrag = y;
+ }
+ });
+
+ while (!window.shouldClose()) {
+ window.update();
+ DrawLevel(bitmap, tiles, level, xOffs, yOffs);
+ car.update(level);
+ car.draw(bitmap, xOffs, yOffs);
+ window.draw(bitmap);
+ }
+
+ return 0;
+}
diff --git a/src/train.cpp b/src/train.cpp
new file mode 100644
index 0000000..e369887
--- /dev/null
+++ b/src/train.cpp
@@ -0,0 +1,52 @@
+#include "train.h"
+
+#include "level.h"
+#include "bitmap.h"
+
+void TrainCar::update(Level& level)
+{
+ auto tile = level.get(x, y);
+ if (tile == 0)
+ return;
+
+ if (m_progress < 12) {
+ m_progress++;
+ return;
+ }
+ else {
+ m_progress = 0;
+ }
+
+ auto n = level.get(x, y - 1);
+ auto e = level.get(x + 1, y);
+ auto s = level.get(x, y + 1);
+ auto w = level.get(x - 1, y);
+
+ if (m_dir == North) {
+ if (n == SouthEast) m_dir = East;
+ if (n == SouthWest) m_dir = West;
+ y--;
+ }
+ else if (m_dir == East) {
+ if (e == NorthWest) m_dir = North;
+ if (e == SouthWest) m_dir = South;
+ x++;
+ }
+ else if (m_dir == South) {
+ if (s == NorthEast) m_dir = East;
+ if (s == NorthWest) m_dir = West;
+ y++;
+ }
+ else if (m_dir == West) {
+ if (w == NorthEast) m_dir = North;
+ if (w == SouthEast) m_dir = South;
+ x--;
+ }
+}
+
+void TrainCar::draw(Bitmap& bitmap, int xo, int yo)
+{
+ int xx = (x - y) * (24 / 2) - xo;
+ int yy = (x + y) * (24 / 4) - yo;
+ bitmap.blit(m_sprite, xx, yy, 0, 0, 24, 24);
+} \ No newline at end of file
diff --git a/src/train.h b/src/train.h
new file mode 100644
index 0000000..ed0de94
--- /dev/null
+++ b/src/train.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "bitmap.h"
+
+enum CarDirection {
+ North,
+ East,
+ South,
+ West
+};
+
+class Level;
+
+class TrainCar {
+public:
+ TrainCar(int x, int y)
+ : x(x), y(y), m_sprite("../res/car.png") {}
+
+ void update(Level&);
+ void draw(Bitmap&, int, int);
+
+ int x, y;
+private:
+ Bitmap m_sprite;
+ int m_progress;
+ CarDirection m_dir;
+};
diff --git a/src/window.cpp b/src/window.cpp
new file mode 100644
index 0000000..a907aba
--- /dev/null
+++ b/src/window.cpp
@@ -0,0 +1,61 @@
+#include "window.h"
+
+#include <SDL2/SDL.h>
+#include <cstdio>
+
+#include "bitmap.h"
+
+Window::Window(const char* title, int width, int height, int scale)
+ : m_width(width), m_height(height), m_scale(scale)
+{
+ if (SDL_Init(SDL_INIT_VIDEO) != 0) {
+ fprintf(stderr, "Failed to initalize SDL: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+ m_window = SDL_CreateWindow("Nonortho", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width * scale, height * scale, SDL_WINDOW_SHOWN);
+ m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED);
+ m_texture = SDL_CreateTexture(m_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height);
+
+ m_isRunning = true;
+}
+
+Window::~Window()
+{
+ SDL_DestroyTexture(m_texture);
+ SDL_DestroyRenderer(m_renderer);
+ SDL_DestroyWindow(m_window);
+ SDL_Quit();
+}
+
+void Window::update()
+{
+ static SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_QUIT:
+ m_isRunning = false;
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ m_mouseDown(event.button.button, event.button.x, event.button.y);
+ break;
+ case SDL_MOUSEMOTION:
+ m_mouseMove(event.button.x, event.button.y);
+ break;
+ case SDL_MOUSEBUTTONUP:
+ m_mouseUp(event.button.button, event.button.x, event.button.y);
+ break;
+ }
+ }
+
+ SDL_UpdateWindowSurface(m_window);
+ SDL_Delay(10);
+}
+
+void Window::draw(Bitmap& bitmap)
+{
+ SDL_RenderClear(m_renderer);
+ SDL_UpdateTexture(m_texture, NULL, bitmap.data, m_width * 4);
+ SDL_RenderCopy(m_renderer, m_texture, NULL, NULL);
+ SDL_RenderPresent(m_renderer);
+} \ No newline at end of file
diff --git a/src/window.h b/src/window.h
new file mode 100644
index 0000000..bdd1e91
--- /dev/null
+++ b/src/window.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <functional>
+
+class Bitmap;
+
+struct SDL_Window;
+struct SDL_Renderer;
+struct SDL_Texture;
+
+class Window {
+public:
+ Window(const char* title, int width, int height, int scale);
+ ~Window();
+
+ void update();
+ void draw(Bitmap&);
+
+ bool shouldClose() { return !m_isRunning; }
+
+ void onMouseDown(std::function<void(int, int, int)> callback) { m_mouseDown = callback; }
+ void onMouseUp(std::function<void(int, int, int)> callback) { m_mouseUp = callback; }
+ void onMouseMove(std::function<void(int, int)> callback) { m_mouseMove = callback; }
+private:
+ bool m_isRunning;
+
+ int m_width, m_height, m_scale;
+
+ SDL_Window* m_window;
+ SDL_Renderer* m_renderer;
+ SDL_Texture* m_texture;
+
+ std::function<void(int, int, int)> m_mouseDown;
+ std::function<void(int, int, int)> m_mouseUp;
+ std::function<void(int, int)> m_mouseMove;
+}; \ No newline at end of file