summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--build.zig107
m---------raylib0
-rw-r--r--src/context.zig54
-rw-r--r--src/main.zig31
-rw-r--r--src/root.zig159
6 files changed, 354 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..4f5b50c
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "raylib"]
+ path = raylib
+ url = git@github.com:AlecGoncharow/raylib.git
diff --git a/build.zig b/build.zig
new file mode 100644
index 0000000..fb83a07
--- /dev/null
+++ b/build.zig
@@ -0,0 +1,107 @@
+const std = @import("std");
+
+const raySdk = @import("raylib/src/build.zig");
+
+// Although this function looks imperative, note that its job is to
+// declaratively construct a build graph that will be executed by an external
+// runner.
+pub fn build(b: *std.Build) void {
+ // Standard target options allows the person running `zig build` to choose
+ // what target to build for. Here we do not override the defaults, which
+ // means any target is allowed, and the default is native. Other options
+ // for restricting supported target set are available.
+ const target = b.standardTargetOptions(.{});
+
+ // Standard optimization options allow the person running `zig build` to select
+ // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
+ // set a preferred release mode, allowing the user to decide how to optimize.
+ const optimize = b.standardOptimizeOption(.{});
+ const defaults = raySdk.Options{};
+ const options = raySdk.Options{
+ .platform_drm = b.option(bool, "platform_drm", "Compile raylib in native mode (no X11)") orelse defaults.platform_drm,
+ .raudio = b.option(bool, "raudio", "Compile with audio support") orelse defaults.raudio,
+ .rmodels = b.option(bool, "rmodels", "Compile with models support") orelse defaults.rmodels,
+ .rtext = b.option(bool, "rtext", "Compile with text support") orelse defaults.rtext,
+ .rtextures = b.option(bool, "rtextures", "Compile with textures support") orelse defaults.rtextures,
+ .rshapes = b.option(bool, "rshapes", "Compile with shapes support") orelse defaults.rshapes,
+ .raygui = b.option(bool, "raygui", "Compile with raygui support") orelse defaults.raygui,
+ };
+ const raylib = raySdk.addRaylib(b, target, optimize, options);
+
+ const lib = b.addStaticLibrary(.{
+ .name = "zigdown",
+ // In this case the main source file is merely a path, however, in more
+ // complicated build scripts, this could be a generated file.
+ .root_source_file = .{ .path = "src/root.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ // This declares intent for the library to be installed into the standard
+ // location when the user invokes the "install" step (the default step when
+ // running `zig build`).
+ b.installArtifact(lib);
+
+ const exe = b.addExecutable(.{
+ .name = "zig-raylib",
+ // In this case the main source file is merely a path, however, in more
+ // complicated build scripts, this could be a generated file.
+ .root_source_file = .{ .path = "src/main.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+ exe.addIncludePath(.{ .path = "raylib/src" });
+ exe.linkLibrary(raylib);
+
+ // This declares intent for the executable to be installed into the
+ // standard location when the user invokes the "install" step (the default
+ // step when running `zig build`).
+ b.installArtifact(exe);
+
+ // This *creates* a Run step in the build graph, to be executed when another
+ // step is evaluated that depends on it. The next line below will establish
+ // such a dependency.
+ const run_cmd = b.addRunArtifact(exe);
+
+ // By making the run step depend on the install step, it will be run from the
+ // installation directory rather than directly from within the cache directory.
+ // This is not necessary, however, if the application depends on other installed
+ // files, this ensures they will be present and in the expected location.
+ run_cmd.step.dependOn(b.getInstallStep());
+
+ // This allows the user to pass arguments to the application in the build
+ // command itself, like this: `zig build run -- arg1 arg2 etc`
+ if (b.args) |args| {
+ run_cmd.addArgs(args);
+ }
+
+ // This creates a build step. It will be visible in the `zig build --help` menu,
+ // and can be selected like this: `zig build run`
+ // This will evaluate the `run` step rather than the default, which is "install".
+ const run_step = b.step("run", "Run the app");
+ run_step.dependOn(&run_cmd.step);
+
+ // Creates a step for unit testing. This only builds the test executable
+ // but does not run it.
+ const lib_unit_tests = b.addTest(.{
+ .root_source_file = .{ .path = "src/root.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+ const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
+
+ const unit_tests = b.addTest(.{
+ .root_source_file = .{ .path = "src/main.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const run_unit_tests = b.addRunArtifact(unit_tests);
+
+ // Similar to creating the run step earlier, this exposes a `test` step to
+ // the `zig build --help` menu, providing a way for the user to request
+ // running the unit tests.
+ const test_step = b.step("test", "Run unit tests");
+ test_step.dependOn(&run_lib_unit_tests.step);
+ test_step.dependOn(&run_unit_tests.step);
+}
diff --git a/raylib b/raylib
new file mode 160000
+Subproject 6aee318d20e9dae05ab6c16cf78f1371281c586
diff --git a/src/context.zig b/src/context.zig
new file mode 100644
index 0000000..f3e88f2
--- /dev/null
+++ b/src/context.zig
@@ -0,0 +1,54 @@
+const std = @import("std");
+const rl = @cImport(@cInclude("raylib.h"));
+const root = @import("root.zig");
+
+pub var camera: rl.Camera2D = undefined;
+pub var grid: []root.MyRect = undefined;
+
+pub const Grid = struct {
+ buffer: std.MultiArrayList(root.Hex),
+ cursor: usize,
+ size: usize,
+ const Self = @This();
+
+ const GridError = error{
+ OutOfBounds,
+ };
+
+ //TODO think about what size really means
+ pub fn init(
+ allocator: std.mem.Allocator,
+ size: usize,
+ ) !Grid {
+ var buffer: std.MultiArrayList(root.Hex) = .{};
+ try buffer.ensureTotalCapacity(allocator, size * size);
+ return .{
+ .buffer = buffer,
+ .cursor = 0,
+ .size = size,
+ };
+ }
+
+ // should only be used when standing up the buffer, afterwards should use q, r indexing
+ pub fn initPush(self: *Self, hex: root.Hex) GridError!void {
+ if (self.cursor >= self.size * 2) {
+ return GridError.OutOfBounds;
+ }
+ self.buffer.insertAssumeCapacity(self.cursor, hex);
+ }
+
+ pub fn set(self: *Self, hex: root.Hex) GridError!void {
+ const idx = hex.qr[1] * self.size + hex.qr[0];
+ if (idx >= self.size * 2) {
+ return GridError.OutOfBounds;
+ }
+
+ self.buffer.set(idx, hex);
+ }
+};
+pub var hex_grid: Grid = undefined;
+pub var hovered_coords: root.HexCoord = root.HexCoord{ .q = 0, .r = 0 };
+pub var hovered_handle: ?usize = null;
+
+var Gpa = std.heap.GeneralPurposeAllocator(.{}){};
+pub var gpa: std.mem.Allocator = Gpa.allocator();
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000..a3cfd2f
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+const rl = @cImport(@cInclude("raylib.h"));
+const root = @import("root.zig");
+const context = @import("context.zig");
+
+pub fn main() !void {
+ const win_width = 960;
+ const win_height = 540;
+ rl.InitWindow(win_width, win_height, "shipit");
+
+ rl.SetTargetFPS(60);
+ defer rl.CloseWindow();
+
+ // Game State Initialization
+ try root.setup();
+ //
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!rl.WindowShouldClose()) {
+ try root.update();
+ try root.draw();
+ }
+}
+
+test "simple test" {
+ var list = std.ArrayList(i32).init(std.testing.allocator);
+ defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
+ try list.append(42);
+ try std.testing.expectEqual(@as(i32, 42), list.pop());
+}
diff --git a/src/root.zig b/src/root.zig
new file mode 100644
index 0000000..286c0c2
--- /dev/null
+++ b/src/root.zig
@@ -0,0 +1,159 @@
+const std = @import("std");
+const rl = @cImport(@cInclude("raylib.h"));
+const context = @import("context.zig");
+
+const grid_size: usize = 21;
+const grid_central_row = grid_size / 2 + 1;
+
+pub const MyRect = struct {
+ rect: rl.Rectangle,
+ color: rl.Color,
+};
+
+const hex_radius: f32 = 100.0;
+const hex_rotation = 30.0;
+
+pub const HexCoord = struct {
+ q: i32,
+ r: i32,
+ pub inline fn fracS(q: f32, r: f32) f32 {
+ return -q - r;
+ }
+
+ pub inline fn index(self: HexCoord) usize {
+ return self.r / grid_size + self.q % grid_size;
+ }
+
+ pub inline fn qrToWorld(q: i32, r: i32) rl.Vector2 {
+ return .{
+ .x = hex_radius * (@sqrt(3.0) * @as(f32, @floatFromInt(q)) + @sqrt(3.0) / 2.0 * @as(f32, @floatFromInt(r))),
+ .y = hex_radius * (3.0 / 2.0 * @as(f32, @floatFromInt(r))),
+ };
+ }
+ pub inline fn worldToQr(point: rl.Vector2) HexCoord {
+ const q = (@sqrt(3.0) / 3.0 * point.x - 1.0 / 3.0 * point.y) / hex_radius;
+ const r = (2.0 / 3.0 * point.y) / hex_radius;
+
+ return axialRound(.{ .x = q, .y = r });
+ }
+ pub inline fn axialRound(frac: rl.Vector2) HexCoord {
+ return cubeRound(rl.Vector3{ .x = frac.x, .y = frac.y, .z = fracS(frac.x, frac.y) });
+ }
+
+ pub inline fn cubeRound(frac: rl.Vector3) HexCoord {
+ var q = @round(frac.x);
+ var r = @round(frac.y);
+ var s = @round(frac.z);
+
+ const q_diff = @abs(q - frac.x);
+ const r_diff = @abs(r - frac.y);
+ const s_diff = @abs(s - frac.z);
+
+ if (q_diff > r_diff and q_diff > s_diff) {
+ q = -r - s;
+ } else if (r_diff > s_diff) {
+ r = -q - s;
+ } else {
+ s = -q - r;
+ }
+
+ return .{
+ .q = @as(i32, @intFromFloat(q)),
+ .r = @as(i32, @intFromFloat(r)),
+ };
+ }
+};
+
+pub const Hex = struct {
+ // todo perhaps worth having a sentiental value somewhere to state if a hex is alive or perhaps make the list optinals?
+ color: rl.Color,
+};
+
+pub fn setup() !void {
+ const target = HexCoord.qrToWorld(grid_central_row, grid_central_row);
+ context.camera = rl.Camera2D{ .target = target, .offset = rl.Vector2{ .x = 0, .y = 0 }, .rotation = 0, .zoom = 1 };
+ context.hex_grid = try context.Grid.init(context.gpa, grid_size);
+
+ // TODO think what it means to populate a hex grid
+ for (0..grid_size) |_| {
+ for (0..grid_size) |_| {
+ try context.hex_grid.initPush(Hex{
+ // TODO real colors
+ .color = rl.DARKGREEN,
+ });
+ }
+ }
+}
+
+pub fn update() !void {
+ //----------------------------------------------------------------------------------
+
+ const mouse_pos = rl.GetMousePosition();
+ const mouse_world_pos = rl.GetScreenToWorld2D(mouse_pos, context.camera);
+
+ context.hovered_coords = HexCoord.worldToQr(mouse_world_pos);
+
+ const zoom_scale = context.camera.zoom;
+ if (rl.IsKeyDown(rl.KEY_D)) {
+ context.camera.target.x += 2 / zoom_scale;
+ }
+ if (rl.IsKeyDown(rl.KEY_A)) {
+ context.camera.target.x -= 2 / zoom_scale;
+ }
+ if (rl.IsKeyDown(rl.KEY_W)) {
+ context.camera.target.y -= 2 / zoom_scale;
+ }
+ if (rl.IsKeyDown(rl.KEY_S)) {
+ context.camera.target.y += 2 / zoom_scale;
+ }
+
+ if (rl.IsMouseButtonDown(rl.MOUSE_BUTTON_MIDDLE)) {
+ const delta = rl.GetMouseDelta();
+ context.camera.target.x -= delta.x / zoom_scale;
+ context.camera.target.y -= delta.y / zoom_scale;
+ }
+
+ const wm = rl.GetMouseWheelMove();
+ if (wm != 0.0) {
+ context.camera.zoom += wm * 0.05;
+
+ context.camera.target = mouse_world_pos;
+ context.camera.offset = mouse_pos;
+
+ if (context.camera.zoom > 3.0) context.camera.zoom = 3.0;
+ if (context.camera.zoom < 0.1) context.camera.zoom = 0.1;
+ }
+}
+pub fn draw() !void {
+ rl.BeginDrawing();
+
+ rl.ClearBackground(rl.SKYBLUE);
+
+ rl.BeginMode2D(context.camera);
+ const slice = context.hex_grid.buffer.slice();
+ const colors = slice.items(.color);
+ for (0..grid_size) |r| {
+ var start: usize = 0;
+ var end: usize = 0;
+ if (r < grid_central_row) {
+ start = grid_central_row - r;
+ end = grid_size;
+ } else {
+ start = 0;
+ end = grid_size - (r - grid_central_row);
+ }
+ for (start..end) |q| {
+ const idx = r * grid_size + q;
+ const center = HexCoord.qrToWorld(@intCast(q), @intCast(r));
+ rl.DrawPoly(center, 6, hex_radius, hex_rotation, colors[idx]);
+ rl.DrawPolyLines(center, 6, hex_radius, hex_rotation, rl.BLACK);
+ }
+ }
+
+ const center = HexCoord.qrToWorld(@intCast(context.hovered_coords.q), @intCast(context.hovered_coords.r));
+ rl.DrawPolyLines(center, 6, hex_radius, hex_rotation, rl.WHITE);
+
+ rl.EndMode2D();
+
+ rl.EndDrawing();
+}