Basic setup.

- Allegro wrapper.
- Setting up of new window etc.
This commit is contained in:
Sander Schobers 2023-06-02 12:21:32 +02:00
commit 5bfa8b2c51
26 changed files with 1717 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.vscode
zig-cache
zig-out
allegro.path

15
README.md Normal file
View File

@ -0,0 +1,15 @@
# TINS 2023
# Building
For building you need:
- A recent (0.11.0-dev) Zig compiler.
- Allegro 5.28 development libraries.
You can use `allegro.path` if your Allegro development libraries can't be natively found. In this file you can put the path to a directory contain the Allegro `lib`, `bin` and `include` folders.
# Licenses
## Cabin font
[Open Font License version 1.1](src/assets/fonts/Cabin-OFL.txt)

0
allegro.path Normal file
View File

697
allegro/allegro.zig Normal file
View File

@ -0,0 +1,697 @@
const std = @import("std");
pub const c = @import("c.zig").c;
pub const Bitmap = struct {
native: *c.ALLEGRO_BITMAP,
pub fn destroy(self: Bitmap) void {
destroyBitmap(self);
}
pub fn draw(self: Bitmap, x: f32, y: f32, flags: DrawFlags) void {
drawBitmap(self, x, y, flags);
}
pub fn drawCentered(self: Bitmap, x: f32, y: f32) void {
self.draw(x - 0.5 * @intToFloat(f32, self.width()), y - 0.5 * @intToFloat(f32, self.height()), DrawFlags{});
}
pub fn drawCenteredScaled(self: Bitmap, x: f32, y: f32, s: f32) void {
const w = s * @intToFloat(f32, self.width());
const h = s * @intToFloat(f32, self.height());
self.drawScaled(x - 0.5 * w, y - 0.5 * h, w, h, DrawFlags{});
}
pub fn drawScaled(self: Bitmap, x: f32, y: f32, w: f32, h: f32, flags: DrawFlags) void {
drawScaledBitmap(self, 0, 0, @intToFloat(f32, self.width()), @intToFloat(f32, self.height()), x, y, w, h, flags);
}
pub fn drawTinted(self: Bitmap, tint: Color, x: f32, y: f32, flags: DrawFlags) void {
drawTintedBitmap(self, tint, x, y, flags);
}
pub fn drawTintedCentered(self: Bitmap, tint: Color, x: f32, y: f32) void {
self.drawTinted(tint, x - 0.5 * @intToFloat(f32, self.width()), y - 0.5 * @intToFloat(f32, self.height()), DrawFlags{});
}
pub fn drawTintedCenteredScaled(self: Bitmap, tint: Color, x: f32, y: f32, s: f32) void {
const w = s * @intToFloat(f32, self.width());
const h = s * @intToFloat(f32, self.height());
self.drawTintedScaled(tint, x - 0.5 * w, y - 0.5 * h, w, h, DrawFlags{});
}
pub fn drawTintedScaled(self: Bitmap, tint: Color, x: f32, y: f32, w: f32, h: f32, flags: DrawFlags) void {
drawTintedScaledBitmap(self, tint, 0, 0, @intToFloat(f32, self.width()), @intToFloat(f32, self.height()), x, y, w, h, flags);
}
pub fn height(self: Bitmap) i32 {
return getBitmapHeight(self);
}
pub fn width(self: Bitmap) i32 {
return getBitmapWidth(self);
}
};
pub const Color = struct {
native: c.ALLEGRO_COLOR,
fn hexToU4(hex: u8) u4 {
return switch (hex) {
'0'...'9' => @intCast(u4, hex - '0'),
'a'...'f' => @intCast(u4, hex - 'a') + 10,
'A'...'F' => @intCast(u4, hex - 'A') + 10,
else => unreachable,
};
}
pub fn initFromHex(hex: []const u8) Color {
if (hex.len == 0) unreachable;
const left: usize = if (hex[0] == '#') 1 else 0;
if (hex.len < left + 6) unreachable;
const r = (@intCast(u8, hexToU4(hex[left])) << 4) + @intCast(u8, hexToU4(hex[left + 1]));
const g = (@intCast(u8, hexToU4(hex[left + 2])) << 4) + @intCast(u8, hexToU4(hex[left + 3]));
const b = (@intCast(u8, hexToU4(hex[left + 4])) << 4) + @intCast(u8, hexToU4(hex[left + 5]));
if (hex.len < left + 8) {
return mapRgb(r, g, b);
}
const a = (@intCast(u8, hexToU4(hex[left + 6])) << 4) + @intCast(u8, hexToU4(hex[left + 7]));
return mapRgba(r, g, b, a);
}
};
pub const Display = struct {
native: *c.ALLEGRO_DISPLAY,
pub fn destroy(self: Display) void {
destroyDisplay(self);
}
pub fn eventSource(self: Display) EventSource {
return getDisplayEventSource(self);
}
pub fn height(self: Display) i32 {
return getDisplayHeight(self);
}
pub fn width(self: Display) i32 {
return getDisplayWidth(self);
}
};
pub const DrawFlags = packed struct(c_int) {
FLIP_HORIZONTAL: bool = false, // 0x00001
FLIP_VERTICAL: bool = false, // 0x00002
_NOT_USED: u30 = 0,
};
pub const DrawTextFlags = enum(c_int) {
NO_KERNING = -1,
ALIGN_LEFT = 0,
ALIGN_CENTER = 1,
ALIGN_RIGHT = 2,
ALIGN_LEFT_INTEGER = 4,
ALIGN_CENTER_INTEGER = 5,
ALIGN_RIGHT_INTEGER = 6,
};
pub const Event = c.ALLEGRO_EVENT;
pub const EventQueue = struct {
native: *c.ALLEGRO_EVENT_QUEUE,
pub fn destroy(self: EventQueue) void {
destroyEventQueue(self);
}
pub fn drop(self: EventQueue) void {
dropNextEvent(self);
}
pub fn flush(self: EventQueue) void {
flushEventQueue(self);
}
pub fn get(self: EventQueue, event: *Event) bool {
return getNextEvent(self, event);
}
pub fn isEmpty(self: EventQueue) bool {
return isEventQueueEmpty(self);
}
pub fn peek(self: EventQueue, event: *Event) void {
peekNextEvent(self, event);
}
pub fn register(self: EventQueue, source: EventSource) void {
registerEventSource(self, source);
}
pub fn registerDisplay(self: EventQueue, display: Display) void {
self.register(display.eventSource());
}
pub fn registerKeyboard(self: EventQueue) void {
self.register(getKeyboardEventSource());
}
pub fn registerMouse(self: EventQueue) void {
self.register(getMouseEventSource());
}
pub fn registerTimer(self: EventQueue, timer: Timer) void {
self.register(timer);
}
pub fn unregister(self: EventQueue, source: EventSource) void {
unregisterEventSource(self, source);
}
pub fn unregisterDisplay(self: EventQueue, display: Display) void {
self.unregister(display.eventSource());
}
pub fn wait(self: EventQueue, event: *Event) void {
waitForEvent(self, event);
}
};
pub const EventSource = struct {
native: *c.ALLEGRO_EVENT_SOURCE,
};
pub const File = struct {
native: *c.ALLEGRO_FILE,
pub fn close(file: File) void {
fclose(file);
}
};
pub const Font = struct {
native: *c.ALLEGRO_FONT,
pub fn destroy(self: Font) void {
destroyFont(self);
}
pub fn draw(self: Font, color: Color, x: f32, y: f32, flags: DrawTextFlags, text: [*:0]const u8) void {
drawText(self, color, x, y, flags, text);
}
pub fn textWidth(self: Font, text: [*:0]const u8) i32 {
return getTextWidth(self, text);
}
};
pub const LoadBitmapFlags = packed struct(c_int) {
_NOT_USED_1: bool = false,
KEEP_BITMAP_FORMAT: bool = false, // 0x0002 // was a bitmap flag in 5.0
_NOT_USED_2: u7 = 0,
NO_PREMULTIPLIED_ALPHA: bool = false, // 0x0200 // was a bitmap flag in 5.0
_NOT_USED_3: bool = false,
KEEP_INDEX: bool = false, // 0x0800
_NOT_USED_4: u20 = 0,
};
pub const LoadTtfFontFlags = packed struct(c_int) {
NO_KERNING: bool = false,
MONOCHROME: bool = false,
NO_AUTOHINT: bool = false,
_NOT_USED: u29 = 0,
};
pub const NewBitmapFlags = packed struct(c_int) {
MEMORY_BITMAP: bool = false, // 0x0001,
_KEEP_BITMAP_FORMAT: bool = false, // 0x0002,
FORCE_LOCKING: bool = false, // 0x0004,
NO_PRESERVE_TEXTURE: bool = false, // 0x0008,
_ALPHA_TEST: bool = false, // 0x0010,
_INTERNAL_OPENGL: bool = false, // 0x0020,
MIN_LINEAR: bool = false, // 0x0040,
MAG_LINEAR: bool = false, // 0x0080,
MIPMAP: bool = false, // 0x0100,
_NO_PREMULTIPLIED_ALPHA: bool = false, // 0x0200,
VIDEO_BITMAP: bool = false, // 0x0400,
_NOT_USED_1: bool = false, // 0x0800
CONVERT_BITMAP: bool = false, // 0x1000,
_NOT_USED_4: u19 = 0,
};
pub const NewDisplayFlags = packed struct(c_int) {
WINDOWED: bool = false,
FULLSCREEN: bool = false,
OPENGL: bool = false,
DIRECT3D_INTERNAL: bool = false,
RESIZABLE: bool = false,
FRAMELESS: bool = false,
GENERATE_EXPOSE_EVENTS: bool = false,
OPENGL_3_0: bool = false,
OPENGL_FORWARD_COMPATIBLE: bool = false,
FULLSCREEN_WINDOW: bool = false,
MINIMIZED: bool = false,
PROGRAMMABLE_PIPELINE: bool = false,
GTK_TOPLEVEL_INTERNAL: bool = false,
MAXIMIZED: bool = false,
OPENGL_ES_PROFILE: bool = false,
OPENGL_CORE_PROFILE: bool = false,
_NOT_USED: u16 = 0,
};
pub const NewDisplayOption = enum(c_int) {
RED_SIZE, // 0
GREEN_SIZE, // 1
BLUE_SIZE, // 2
ALPHA_SIZE, // 3
RED_SHIFT, // 4
GREEN_SHIFT, // 5
BLUE_SHIFT, // 6
ALPHA_SHIFT, // 7
ACC_RED_SIZE, // 8
ACC_GREEN_SIZE, // 9
ACC_BLUE_SIZE, // 10
ACC_ALPHA_SIZE, // 11
STEREO, // 12
AUX_BUFFERS, // 13
COLOR_SIZE, // 14
DEPTH_SIZE, // 15
STENCIL_SIZE, // 16
SAMPLE_BUFFERS, // 17
SAMPLES, // 18
RENDER_METHOD, // 19
FLOAT_COLOR, // 20
FLOAT_DEPTH, // 21
SINGLE_BUFFER, // 22
SWAP_METHOD, // 23
COMPATIBLE_DISPLAY, // 24
UPDATE_DISPLAY_REGION, // 25
VSYNC, // 26
MAX_BITMAP_SIZE, // 27
SUPPORT_NPOT_BITMAP, // 28
CAN_DRAW_INTO_BITMAP, // 29
SUPPORT_SEPARATE_ALPHA, // 30
AUTO_CONVERT_BITMAPS, // 31
SUPPORTED_ORIENTATIONS, // 32
OPENGL_MAJOR_VERSION, // 33
OPENGL_MINOR_VERSION, // 34
DEFAULT_SHADER_PLATFORM, // 35
};
pub const OptionImportance = enum(c_int) {
DONTCARE, // 0
REQUIRE, // 1
SUGGEST, // 2
};
pub const PixelFormat = enum(c_int) {
ANY, // 0
ANY_NO_ALPHA, // 1
ANY_WITH_ALPHA, // 2
ANY_15_NO_ALPHA, // 3
ANY_16_NO_ALPHA, // 4
ANY_16_WITH_ALPHA, // 5
ANY_24_NO_ALPHA, // 6
ANY_32_NO_ALPHA, // 7
ANY_32_WITH_ALPHA, // 8
ARGB_8888, // 9
RGBA_8888, // 10
ARGB_4444, // 11
RGB_888, // 12 // 24 bit format
RGB_565, // 13
RGB_555, // 14
RGBA_5551, // 15
ARGB_1555, // 16
ABGR_8888, // 17
XBGR_8888, // 18
BGR_888, // 19 // 24 bit format
BGR_565, // 20
BGR_555, // 21
RGBX_8888, // 22
XRGB_8888, // 23
ABGR_F32, // 24
ABGR_8888_LE, // 25
RGBA_4444, // 26
SINGLE_CHANNEL_8, // 27
COMPRESSED_RGBA_DXT1, // 28
COMPRESSED_RGBA_DXT3, // 29
COMPRESSED_RGBA_DXT5, // 30
ALLEGRO_NUM_PIXEL_FORMATS,
};
pub const Timer = struct {
native: *c.ALLEGRO_TIMER,
pub fn destroy(self: Timer) void {
destroyTimer(self);
}
pub fn eventSource(self: Timer) EventSource {
return getTimerEventSource(self);
}
pub fn start(self: Timer) void {
startTimer(self);
}
};
pub fn acknowledgeResize(display: Display) bool {
return c.al_acknowledge_resize(display.native);
}
pub fn clearToColor(color: Color) void {
c.al_clear_to_color(color.native);
}
pub fn convertMemoryBitmaps() void {
c.al_convert_memory_bitmaps();
}
pub fn createBitmap(width: i32, height: i32) !Bitmap {
const bitmap = c.al_create_bitmap(width, height);
if (bitmap) |native| {
return Bitmap{ .native = native };
}
return error.FailedToCreateBitmap;
}
pub fn createDisplay(width: i32, height: i32) !Display {
const display = c.al_create_display(width, height);
if (display) |native| {
return Display{ .native = native };
}
return error.FailedToCreateDisplay;
}
pub fn createEventQueue() !EventQueue {
const queue = c.al_create_event_queue();
if (queue) |native| {
return EventQueue{ .native = native };
}
return error.FailedToCreateEventQueue;
}
pub fn createTimer(interval: f32) !Timer {
const timer = c.al_create_timer(interval);
if (timer) |native| {
return Timer{ .native = native };
}
return error.FailedToCreateTimer;
}
pub fn destroyBitmap(bitmap: Bitmap) void {
c.al_destroy_bitmap(bitmap.native);
}
pub fn destroyDisplay(display: Display) void {
c.al_destroy_display(display.native);
}
pub fn destroyEventQueue(queue: EventQueue) void {
c.al_destroy_event_queue(queue.native);
}
pub fn destroyFont(font: Font) void {
c.al_destroy_font(font.native);
}
pub fn destroyTimer(timer: Timer) void {
c.al_destroy_timer(timer.native);
}
pub fn drawBitmap(bitmap: Bitmap, dx: f32, dy: f32, flags: DrawFlags) void {
c.al_draw_bitmap(bitmap.native, dx, dy, @bitCast(c_int, flags));
}
pub fn drawScaledBitmap(bitmap: Bitmap, sx: f32, sy: f32, sw: f32, sh: f32, dx: f32, dy: f32, dw: f32, dh: f32, flags: DrawFlags) void {
c.al_draw_scaled_bitmap(bitmap.native, sx, sy, sw, sh, dx, dy, dw, dh, @bitCast(c_int, flags));
}
pub fn drawText(font: Font, color: Color, x: f32, y: f32, flags: DrawTextFlags, text: [*:0]const u8) void {
c.al_draw_text(font.native, color.native, x, y, @enumToInt(flags), text);
}
pub fn drawTintedBitmap(bitmap: Bitmap, tint: Color, dx: f32, dy: f32, flags: DrawFlags) void {
c.al_draw_tinted_bitmap(bitmap.native, tint.native, dx, dy, @bitCast(c_int, flags));
}
pub fn drawTintedScaledBitmap(bitmap: Bitmap, tint: Color, sx: f32, sy: f32, sw: f32, sh: f32, dx: f32, dy: f32, dw: f32, dh: f32, flags: DrawFlags) void {
c.al_draw_tinted_scaled_bitmap(bitmap.native, tint.native, sx, sy, sw, sh, dx, dy, dw, dh, @bitCast(c_int, flags));
}
pub fn dropNextEvent(queue: EventQueue) bool {
return c.al_drop_next_event(queue.native);
}
pub fn fclose(file: File) bool {
return c.al_fclose(file.native);
}
pub fn flipDisplay() void {
c.al_flip_display();
}
pub fn flushEventQueue(queue: EventQueue) void {
c.al_flush_event_queue(queue.native);
}
pub fn isEventQueueEmpty(queue: EventQueue) bool {
return c.al_is_event_queue_empty(queue.native);
}
pub fn getBackbuffer(display: Display) !Bitmap {
const bitmap = c.al_get_backbuffer(display.native);
if (bitmap) |native| {
return Bitmap{ .native = native };
}
return error.FailedToGetBackBufferForDisplay;
}
pub fn getBitmapHeight(bitmap: Bitmap) c_int {
return c.al_get_bitmap_height(bitmap.native);
}
pub fn getBitmapWidth(bitmap: Bitmap) c_int {
return c.al_get_bitmap_width(bitmap.native);
}
pub fn getCurrentDisplay() !Display {
const display = c.al_get_current_display();
if (display) |native| {
return Display{ .native = native };
}
return error.NoCurrentDisplay;
}
pub fn getDisplayEventSource(display: Display) EventSource {
return EventSource{ .native = c.al_get_display_event_source(display.native) };
}
pub fn getDisplayFlags(display: Display) NewDisplayFlags {
return @bitCast(NewDisplayFlags, c.al_get_display_flags(display.native));
}
pub fn getDisplayHeight(display: Display) i32 {
return c.al_get_display_height(display.native);
}
pub fn getDisplayWidth(display: Display) i32 {
return c.al_get_display_width(display.native);
}
pub fn getFontAscent(font: Font) i32 {
return c.al_get_font_ascent(font.native);
}
pub fn getFontDescent(font: Font) i32 {
return c.al_get_font_descent(font.native);
}
pub fn getFontLineHeight(font: Font) i32 {
return c.al_get_font_line_height(font.native);
}
pub fn getKeyboardEventSource() EventSource {
return EventSource{ .native = c.al_get_keyboard_event_source() };
}
pub fn getNewBitmapFlags() NewBitmapFlags {
return c.al_set_new_bitmap_flags();
}
pub fn getNewDisplayOption(option: NewDisplayOption, importance: *OptionImportance) c_int {
return c.al_get_new_display_option(@enumToInt(option), @ptrCast(*c_int, importance));
}
pub fn getNewBitmapFormat() PixelFormat {
return c.al_set_new_bitmap_format();
}
pub fn getNewDisplayFlags() NewDisplayFlags {
return c.al_get_new_display_flags();
}
pub fn getNextEvent(queue: EventQueue, event: *Event) bool {
return c.al_get_next_event(queue.native, event);
}
pub fn getMouseEventSource() EventSource {
return EventSource{ .native = c.al_get_mouse_event_source() };
}
pub fn getTextWidth(font: Font, text: [*:0]const u8) void {
c.al_get_text_width(font.native, &text[0]);
}
pub fn getTime() f64 {
return c.al_get_time();
}
pub fn getTimerEventSource(timer: Timer) EventSource {
return EventSource{ .native = c.al_get_timer_event_source(timer.native) };
}
pub fn init() bool {
return c.al_init();
}
pub fn initFontAddon() bool {
return c.al_init_font_addon();
}
pub fn initImageAddon() bool {
return c.al_init_image_addon();
}
pub fn initTtfAddon() bool {
return c.al_init_ttf_addon();
}
pub fn installKeyboard() bool {
return c.al_install_keyboard();
}
pub fn installMouse() bool {
return c.al_install_mouse();
}
pub fn loadBitmap(path: [*:0]const u8) !Bitmap {
const bitmap = c.al_load_bitmap(path);
if (bitmap) |native| {
return Bitmap{ .native = native };
}
return error.FailedToLoadBitmap;
}
pub fn loadBitmapF(file: File, ident: []const u8) !Bitmap {
const bitmap = c.al_load_bitmap_f(file.native, &ident[0]);
if (bitmap) |native| {
return Bitmap{ .native = native };
}
return error.FailedToLoadBitmap;
}
pub fn loadBitmapFont(path: [*:0]const u8) !Font {
const font = c.al_load_bitmap_font(path);
if (font) |native| {
return Font{ .native = native };
}
return error.FailedToLoadFont;
}
pub fn loadFont(path: [*:0]const u8, size: i32, flags: c_int) !Font {
const font = c.al_load_font(path, size, flags);
if (font) |native| {
return Font{ .native = native };
}
return error.FailedToLoadFont;
}
pub fn loadTtfFont(path: [*:0]const u8, size: i32, flags: LoadTtfFontFlags) !Font {
const font = c.al_load_ttf_font(path, size, @bitCast(c_int, flags));
if (font) |native| {
return Font{ .native = native };
}
return error.FailedToLoadFont;
}
pub fn mapRgb(r: u8, g: u8, b: u8) Color {
return Color{ .native = c.al_map_rgb(r, g, b) };
}
pub fn mapRgba(r: u8, g: u8, b: u8, a: u8) Color {
return Color{ .native = c.al_map_rgba(r, g, b, a) };
}
pub fn mapRgbaF(r: f32, g: f32, b: f32, a: f32) Color {
return Color{ .native = c.al_map_rgba_f(r, g, b, a) };
}
pub fn mapRgbF(r: f32, g: f32, b: f32) Color {
return Color{ .native = c.al_map_rgb_f(r, g, b) };
}
pub fn openMemfile(data: []u8, mode: []const u8) !File {
const file = c.al_open_memfile(&data[0], @intCast(i64, data.len), &mode[0]);
if (file) |native| {
return File{ .native = native };
}
return error.FailedToOpenMemfile;
}
pub fn peekNextEvent(queue: EventQueue, event: *Event) bool {
return c.al_peek_next_event(queue.native, event);
}
pub fn registerEventSource(queue: EventQueue, source: EventSource) void {
c.al_register_event_source(queue.native, source.native);
}
pub fn setDisplayFlag(display: Display, flag: NewDisplayFlags, on: bool) bool {
return c.al_set_display_flag(display.native, @bitCast(c_int, flag), on);
}
pub fn setNewBitmapFlags(flags: NewBitmapFlags) void {
c.al_set_new_bitmap_flags(@bitCast(c_int, flags));
}
pub fn setNewBitmapFormat(format: PixelFormat) void {
c.al_set_new_bitmap_format(format);
}
pub fn setNewDisplayFlags(flags: NewDisplayFlags) void {
c.al_set_new_display_flags(@bitCast(c_int, flags));
}
pub fn setNewDisplayOption(option: NewDisplayOption, value: i32, importance: OptionImportance) void {
c.al_set_new_display_option(@enumToInt(option), value, @enumToInt(importance));
}
pub fn setTargetBitmap(bitmap: Bitmap) void {
c.al_set_target_bitmap(bitmap.native);
}
pub fn setTargetBackbuffer(display: Display) void {
c.al_set_target_bitmap(display.native);
}
pub fn startTimer(timer: Timer) void {
c.al_start_timer(timer.native);
}
pub fn unregisterEventSource(queue: EventQueue, source: EventSource) void {
c.al_unregister_event_source(queue.native, source.native);
}
pub fn waitForEvent(queue: EventQueue, event: *Event) void {
c.al_wait_for_event(queue.native, event);
}
pub fn waitForVsync() bool {
return c.al_wait_for_vsync();
}

24
allegro/build.zig Normal file
View File

@ -0,0 +1,24 @@
const std = @import("std");
fn current_file() []const u8 {
return @src().file;
}
const cwd = std.fs.path.dirname(current_file()).?;
const path_separator = std.fs.path.sep_str;
/// add this package to exe
pub fn addTo(comptime allegro_dir: []const u8, exe: *std.build.LibExeObjStep) void {
exe.addAnonymousModule("allegro", .{ .source_file = .{ .path = cwd ++ path_separator ++ "allegro.zig" } });
if (allegro_dir.len > 0) {
exe.addIncludePath(allegro_dir ++ path_separator ++ "include");
exe.addLibraryPath(allegro_dir ++ path_separator ++ "lib");
}
exe.linkLibC();
exe.linkSystemLibrary("allegro");
exe.linkSystemLibrary("allegro_font");
exe.linkSystemLibrary("allegro_image");
exe.linkSystemLibrary("allegro_memfile");
exe.linkSystemLibrary("allegro_ttf");
}

8
allegro/c.zig Normal file
View File

@ -0,0 +1,8 @@
pub const c = @cImport({
@cInclude("stdint.h");
@cInclude("allegro5/allegro.h");
@cInclude("allegro5/allegro_font.h");
@cInclude("allegro5/allegro_image.h");
@cInclude("allegro5/allegro_memfile.h");
@cInclude("allegro5/allegro_ttf.h");
});

75
build.zig Normal file
View File

@ -0,0 +1,75 @@
const std = @import("std");
const allegro = @import("allegro/build.zig");
const allegroPath = @embedFile("allegro.path");
// 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 exe = b.addExecutable(.{
.name = "tins2023",
// 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,
});
allegro.addTo(allegroPath, exe);
// 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 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_unit_tests.step);
}

View File

@ -0,0 +1,93 @@
Copyright 2018 The Cabin Project Authors (https://github.com/impallari/Cabin.git)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

92
src/context.zig Normal file
View File

@ -0,0 +1,92 @@
const std = @import("std");
const allegro = @import("allegro");
const engine = @import("engine.zig");
const Palette = @import("palette.zig").Palette;
const Scene = @import("scene.zig").Scene;
pub const Context = struct {
const DefaultDisplayWidth = 1280;
const DefaultDisplayHeight = 720;
allocator: std.mem.Allocator,
palette: Palette = undefined,
shouldQuit: bool = false,
scene: ?Scene = null,
display: allegro.Display,
events: allegro.EventQueue,
viewport: engine.Viewport,
fonts: engine.Fonts,
textures: engine.Textures,
pub fn init(allocator: std.mem.Allocator) !Context {
_ = allegro.init();
_ = allegro.initImageAddon();
_ = allegro.installKeyboard();
_ = allegro.installMouse();
_ = allegro.initFontAddon();
_ = allegro.initTtfAddon();
allegro.setNewDisplayOption(allegro.NewDisplayOption.VSYNC, 1, allegro.OptionImportance.REQUIRE);
allegro.setNewDisplayOption(allegro.NewDisplayOption.SAMPLE_BUFFERS, 1, allegro.OptionImportance.REQUIRE);
allegro.setNewDisplayOption(allegro.NewDisplayOption.SAMPLES, 4, allegro.OptionImportance.REQUIRE);
allegro.setNewDisplayFlags(allegro.NewDisplayFlags{ .RESIZABLE = true });
const viewport = engine.Viewport.init(DefaultDisplayWidth, DefaultDisplayHeight);
const display = try allegro.createDisplay(DefaultDisplayWidth, DefaultDisplayHeight);
const events = try allegro.createEventQueue();
events.registerDisplay(display);
events.registerKeyboard();
events.registerMouse();
return Context{
.allocator = allocator,
.display = display,
.events = events,
.viewport = viewport,
.fonts = engine.Fonts.init(allocator),
.textures = engine.Textures.init(allocator),
};
}
fn exitScene(self: *Context) void {
if (self.scene) |scene| {
scene.exit(self);
scene.deinit();
self.scene = null;
}
}
pub fn deinit(self: *Context) void {
self.events.destroy();
self.display.destroy();
self.exitScene();
self.fonts.deinit();
self.textures.deinit();
}
pub fn quit(self: *Context) void {
self.shouldQuit = true;
}
pub fn resized(self: *Context, width: i32, height: i32) void {
self.viewport.update(width, height);
}
pub fn switchToScene(self: *Context, comptime SceneType: type, build: ?*const fn (*SceneType) void) !void {
self.exitScene();
const scene = try Scene.init(SceneType, self.allocator, build);
self.scene = scene;
scene.enter(self);
}
pub fn toggleFullScreen(self: *Context) void {
var displayFlags = allegro.getDisplayFlags(self.display);
_ = allegro.setDisplayFlag(self.display, allegro.NewDisplayFlags{ .FULLSCREEN_WINDOW = true }, !displayFlags.FULLSCREEN_WINDOW);
self.viewport.update(self.display.width(), self.display.height());
}
};

11
src/engine.zig Normal file
View File

@ -0,0 +1,11 @@
const assets = @import("engine/assets.zig");
pub const Fonts = assets.Fonts;
pub const OpaquePtr = @import("engine/opaque_ptr.zig").OpaquePtr;
pub const Point = @import("engine/point.zig").Point;
pub const PointF = @import("engine/point_f.zig").PointF;
pub const Rectangle = @import("engine/rectangle.zig").Rectangle;
pub const RectangleF = @import("engine/rectangle_f.zig").RectangleF;
pub const Scene = @import("engine/scene.zig").Scene;
pub const Textures = assets.Textures;
pub const Viewport = @import("engine/viewport.zig").Viewport;

97
src/engine/assets.zig Normal file
View File

@ -0,0 +1,97 @@
const std = @import("std");
const allegro = @import("allegro");
pub fn Assets(comptime Asset: type, comptime destroy: *const fn (Asset) void) type {
return struct {
const Self = @This();
assets: std.StringHashMap(Asset),
pub fn init(allocator: std.mem.Allocator) Self {
return Self{
.assets = std.StringHashMap(Asset).init(allocator),
};
}
pub fn deinit(self: *Self) void {
var assets = self.assets.iterator();
while (assets.next()) |entry| {
destroy(entry.value_ptr.*);
}
self.assets.deinit();
}
pub fn get(self: Self, name: []const u8) ?Asset {
return self.assets.get(name);
}
pub fn put(self: *Self, name: []const u8, asset: Asset) !void {
try self.assets.put(name, asset);
}
};
}
pub const Fonts = struct {
fn destroy(font: allegro.Font) void {
font.destroy();
}
const Map = Assets(allegro.Font, Fonts.destroy);
assets: Map,
pub fn init(allocator: std.mem.Allocator) Fonts {
return Fonts{
.assets = Map.init(allocator),
};
}
pub fn deinit(self: *Fonts) void {
self.assets.deinit();
}
pub fn addFromFileTTF(self: *Fonts, name: []const u8, path: [*:0]const u8, size: i32) !void {
const font = try allegro.loadTtfFont(path, size, allegro.LoadTtfFontFlags{});
try self.assets.put(name, font);
}
pub fn get(self: Fonts, name: []const u8) ?allegro.Font {
return self.assets.get(name);
}
};
pub const Textures = struct {
fn destroy(bitmap: allegro.Bitmap) void {
bitmap.destroy();
}
const Map = Assets(allegro.Bitmap, destroy);
assets: Map,
pub fn init(allocator: std.mem.Allocator) Textures {
return Textures{
.assets = Map.init(allocator),
};
}
pub fn deinit(self: *Textures) void {
self.assets.deinit();
}
pub fn addFromFile(self: *Textures, name: []const u8, path: [*:0]const u8) !void {
const bitmap = try allegro.loadBitmap(path);
try self.assets.put(name, bitmap);
}
pub fn addFromMemoryPNG(self: *Textures, name: []const u8, data: []const u8) !void {
const memoryFile = try allegro.openMemfile(@constCast(data), "r");
defer _ = allegro.fclose(memoryFile);
const bitmap = try allegro.loadBitmapF(memoryFile, ".png");
try self.assets.put(name, bitmap);
}
pub fn get(self: Textures, name: []const u8) ?allegro.Bitmap {
return self.assets.get(name);
}
};

43
src/engine/opaque_ptr.zig Normal file
View File

@ -0,0 +1,43 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
pub const OpaquePtr = struct {
allocator: Allocator,
object: usize = undefined,
virtualTable: VirtualTable = undefined,
const VirtualTable = struct {
destroy: *const fn (OpaquePtr) void,
};
pub fn Object(comptime Type: type) type {
return struct {
ptr: *Type,
opaquePtr: OpaquePtr,
};
}
pub fn create(comptime Type: type, allocator: Allocator) !Object(Type) {
const ptr = try allocator.create(Type);
const opaq = OpaquePtr{
.allocator = allocator,
.object = @ptrToInt(ptr),
.virtualTable = .{
.destroy = struct {
fn destroy(self: OpaquePtr) void {
const object = @intToPtr(*Type, self.object);
self.allocator.destroy(object);
}
}.destroy,
},
};
return Object(Type){
.ptr = ptr,
.opaquePtr = opaq,
};
}
pub fn destroy(self: OpaquePtr) void {
self.virtualTable.destroy(self);
}
};

30
src/engine/point.zig Normal file
View File

@ -0,0 +1,30 @@
const PointF = @import("point_f.zig").PointF;
pub const Point = struct {
x: i64,
y: i64,
pub fn init(x: i64, y: i64) Point {
return Point{ .x = x, .y = y };
}
pub fn initUsize(x: usize, y: usize) Point {
return Point.init(@intCast(i64, x), @intCast(i64, y));
}
pub fn add(self: Point, other: Point) Point {
return Point{ .x = self.x + other.x, .y = self.y + other.y };
}
pub fn float(self: Point) PointF {
return PointF{ .x = @intToFloat(f32, self.x), .y = @intToFloat(f32, self.y) };
}
pub fn multiply(self: Point, factor: i64) Point {
return Point{ .x = self.x * factor, .y = self.y * factor };
}
pub fn subtract(self: Point, other: Point) Point {
return Point{ .x = self.x - other.x, .y = self.y - other.y };
}
};

36
src/engine/point_f.zig Normal file
View File

@ -0,0 +1,36 @@
const std = @import("std");
const Point = @import("point.zig").Point;
pub const PointF = struct {
x: f32,
y: f32,
pub fn init(x: f32, y: f32) PointF {
return PointF{ .x = x, .y = y };
}
pub fn add(self: PointF, other: PointF) PointF {
return PointF{ .x = self.x + other.x, .y = self.y + other.y };
}
pub fn ceil(self: PointF) PointF {
return PointF{ .x = std.math.ceil(self.x), .y = std.math.ceil(self.y) };
}
pub fn floor(self: PointF) PointF {
return PointF{ .x = std.math.floor(self.x), .y = std.math.floor(self.y) };
}
pub fn integer(self: PointF) Point {
return Point{ .x = @floatToInt(i64, self.x), .y = @floatToInt(i64, self.y) };
}
pub fn multiply(self: PointF, factor: f32) PointF {
return PointF{ .x = self.x * factor, .y = self.y * factor };
}
pub fn subtract(self: PointF, other: PointF) PointF {
return PointF{ .x = self.x - other.x, .y = self.y - other.y };
}
};

75
src/engine/rectangle.zig Normal file
View File

@ -0,0 +1,75 @@
const Point = @import("point.zig").Point;
pub const Rectangle = struct {
min: Point,
max: Point,
pub fn init(min: Point, max: Point) Rectangle {
if (min.x > max.x) unreachable;
if (min.y > max.y) unreachable;
return Rectangle{ .min = min, .max = max };
}
pub fn initAbsolute(x1: i64, y1: i64, x2: i64, y2: i64) Rectangle {
if (x1 < x2) {
if (y1 < y2) {
return Rectangle{
.min = Point{ .x = x1, .y = y1 },
.max = Point{ .x = x2, .y = y2 },
};
}
return Rectangle{
.min = Point{ .x = x1, .y = y2 },
.max = Point{ .x = x2, .y = y1 },
};
}
if (y1 < y2) {
return Rectangle{
.min = Point{ .x = x2, .y = y1 },
.max = Point{ .x = x1, .y = y2 },
};
}
return Rectangle{
.min = Point{ .x = x2, .y = y2 },
.max = Point{ .x = x1, .y = y1 },
};
}
pub fn initRelative(x: i64, y: i64, w: i64, h: i64) Rectangle {
if (w < 0) {
if (h < 0) {
return Rectangle{
.min = Point{ .x = x + w, .y = y + h },
.max = Point{ .x = x, .y = y },
};
}
return Rectangle{
.min = Point{ .x = x + w, .y = y },
.max = Point{ .x = x, .y = y + h },
};
}
if (h < 0) {
return Rectangle{
.min = Point{ .x = x, .y = y + h },
.max = Point{ .x = x + w, .y = y },
};
}
return Rectangle{
.min = Point{ .x = x, .y = y },
.max = Point{ .x = x + w, .y = y + h },
};
}
pub fn center(self: Rectangle) Point {
return Point.init(@divTrunc(self.min.x + self.max.x, 2), @divTrunc(self.min.y + self.max.y, 2));
}
pub fn height(self: Rectangle) i64 {
return self.max.y - self.min.y;
}
pub fn width(self: Rectangle) i64 {
return self.max.x - self.min.x;
}
};

View File

@ -0,0 +1,75 @@
const PointF = @import("point_f.zig").PointF;
pub const RectangleF = struct {
min: PointF,
max: PointF,
pub fn init(min: PointF, max: PointF) RectangleF {
if (min.x > max.x) unreachable;
if (min.y > max.y) unreachable;
return RectangleF{ .min = min, .max = max };
}
pub fn initAbsolute(x1: f32, y1: f32, x2: f32, y2: f32) RectangleF {
if (x1 < x2) {
if (y1 < y2) {
return RectangleF{
.min = PointF{ .x = x1, .y = y1 },
.max = PointF{ .x = x2, .y = y2 },
};
}
return RectangleF{
.min = PointF{ .x = x1, .y = y2 },
.max = PointF{ .x = x2, .y = y1 },
};
}
if (y1 < y2) {
return RectangleF{
.min = PointF{ .x = x2, .y = y1 },
.max = PointF{ .x = x1, .y = y2 },
};
}
return RectangleF{
.min = PointF{ .x = x2, .y = y2 },
.max = PointF{ .x = x1, .y = y1 },
};
}
pub fn initRelative(x: f32, y: f32, w: f32, h: f32) RectangleF {
if (w < 0) {
if (h < 0) {
return RectangleF{
.min = PointF{ .x = x + w, .y = y + h },
.max = PointF{ .x = x, .y = y },
};
}
return RectangleF{
.min = PointF{ .x = x + w, .y = y },
.max = PointF{ .x = x, .y = y + h },
};
}
if (h < 0) {
return RectangleF{
.min = PointF{ .x = x, .y = y + h },
.max = PointF{ .x = x + w, .y = y },
};
}
return RectangleF{
.min = PointF{ .x = x, .y = y },
.max = PointF{ .x = x + w, .y = y + h },
};
}
pub fn center(self: RectangleF) PointF {
return self.min.add(self.max).multiply(0.5);
}
pub fn height(self: RectangleF) f32 {
return self.max.y - self.min.y;
}
pub fn width(self: RectangleF) f32 {
return self.max.x - self.min.x;
}
};

90
src/engine/scene.zig Normal file
View File

@ -0,0 +1,90 @@
const std = @import("std");
const OpaquePtr = @import("opaque_ptr.zig").OpaquePtr;
pub fn Scene(comptime Context: type, comptime Event: type) type {
return struct {
const Self = @This();
object: usize = undefined,
virtualTable: VirtualTable = undefined,
opaquePtr: ?OpaquePtr = null,
const VirtualTable = struct {
enter: *const fn (Self, *Context) void,
exit: *const fn (Self, *Context) void,
handle: *const fn (Self, *Context, Event) anyerror!void,
update: *const fn (Self, *Context, f32) void,
render: *const fn (Self, *Context) void,
};
pub fn enter(self: Self, ctx: *Context) void {
self.virtualTable.enter(self, ctx);
}
pub fn exit(self: Self, ctx: *Context) void {
self.virtualTable.exit(self, ctx);
}
pub fn handle(self: Self, ctx: *Context, event: Event) !void {
try self.virtualTable.handle(self, ctx, event);
}
pub fn update(self: Self, ctx: *Context, dt: f32) void {
self.virtualTable.update(self, ctx, dt);
}
pub fn render(self: Self, ctx: *Context) void {
self.virtualTable.render(self, ctx);
}
pub fn makeOpaque(comptime SceneType: type, object: *SceneType) Self {
return Self{ .object = @ptrToInt(object), .virtualTable = .{
.enter = struct {
fn enter(self: Self, ctx: *Context) void {
const scene = @intToPtr(*SceneType, self.object);
scene.enter(ctx);
}
}.enter,
.exit = struct {
fn exit(self: Self, ctx: *Context) void {
const scene = @intToPtr(*SceneType, self.object);
scene.exit(ctx);
}
}.exit,
.handle = struct {
fn handle(self: Self, ctx: *Context, event: Event) !void {
const scene = @intToPtr(*SceneType, self.object);
try scene.handle(ctx, event);
}
}.handle,
.update = struct {
fn update(self: Self, ctx: *Context, dt: f32) void {
const scene = @intToPtr(*SceneType, self.object);
scene.update(ctx, dt);
}
}.update,
.render = struct {
fn render(self: Self, ctx: *Context) void {
const scene = @intToPtr(*SceneType, self.object);
scene.render(ctx);
}
}.render,
} };
}
pub fn init(comptime SceneType: type, allocator: std.mem.Allocator, build: ?*const fn (*SceneType) void) !Self {
const object = try OpaquePtr.create(SceneType, allocator);
var scene = Self.makeOpaque(SceneType, object.ptr);
if (build) |b| {
b(object.ptr);
}
return scene;
}
pub fn deinit(self: Self) void {
if (self.opaquePtr) |ptr| {
ptr.destroy();
}
}
};
}

54
src/engine/viewport.zig Normal file
View File

@ -0,0 +1,54 @@
const PointF = @import("point_f.zig").PointF;
const RectangleF = @import("rectangle_f.zig").RectangleF;
pub const Viewport = struct {
unscaledWidth: i32,
unscaledHeight: i32,
actualWidth: i32,
actualHeight: i32,
scaledWidth: i32,
scaledHeight: i32,
bounds: RectangleF,
scale: f32,
pub fn init(width: i32, height: i32) Viewport {
return Viewport{
.unscaledWidth = width,
.unscaledHeight = height,
.actualWidth = width,
.actualHeight = height,
.scaledWidth = width,
.scaledHeight = height,
.bounds = RectangleF.initRelative(0, 0, @intToFloat(f32, width), @intToFloat(f32, height)),
.scale = 1.0,
};
}
pub fn center(self: Viewport) PointF {
return self.bounds.center();
}
pub fn update(self: *Viewport, width: i32, height: i32) void {
self.actualWidth = width;
self.actualHeight = height;
const horizontalRatio = @intToFloat(f32, width) / @intToFloat(f32, self.unscaledWidth);
const verticalRatio = @intToFloat(f32, height) / @intToFloat(f32, self.unscaledHeight);
if (horizontalRatio < verticalRatio) {
self.scaledWidth = width;
self.scaledHeight = @floatToInt(i32, horizontalRatio * @intToFloat(f32, self.unscaledHeight));
const top = @divFloor(height - self.scaledHeight, 2);
self.bounds = RectangleF.initRelative(0, @intToFloat(f32, top), @intToFloat(f32, width), @intToFloat(f32, self.scaledHeight));
self.scale = horizontalRatio;
} else {
self.scaledWidth = @floatToInt(i32, verticalRatio * @intToFloat(f32, self.unscaledWidth));
self.scaledHeight = height;
const left = @divFloor(width - self.scaledWidth, 2);
self.bounds = RectangleF.initRelative(@intToFloat(f32, left), 0, @intToFloat(f32, self.scaledWidth), @intToFloat(f32, height));
self.scale = verticalRatio;
}
}
};

98
src/main.zig Normal file
View File

@ -0,0 +1,98 @@
const std = @import("std");
const allegro = @import("allegro");
const engine = @import("engine.zig");
const Context = @import("context.zig").Context;
const Palette = @import("palette.zig").Palette;
const TitleScene = @import("title_scene.zig").TitleScene;
fn currentFile() []const u8 {
return @src().file;
}
const sourceDir = std.fs.path.dirname(currentFile()).?;
const assetsDir = sourceDir ++ "/assets";
fn hexColor(hex: []const u8) allegro.Color {
return allegro.Color.initFromHex(hex);
}
pub fn main() !void {
// Primary: #551485
// Secondary: #e44b50
// Tertiary: #479be6
// Neutral: #948f94
var arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var context = try Context.init(allocator);
defer context.deinit();
context.palette = Palette{
.primary = .{ .background = hexColor("#7d42ad"), .text = hexColor("#ffffff") },
.primaryContainer = .{ .background = hexColor("#f2daff"), .text = hexColor("#2e004e") },
.secondary = .{ .background = hexColor("#b32731"), .text = hexColor("#ffffff") },
.secondaryContainer = .{ .background = hexColor("#ffdad8"), .text = hexColor("#410006") },
.tertiary = .{ .background = hexColor("#0062a0"), .text = hexColor("#ffffff") },
.tertiaryContainer = .{ .background = hexColor("#d0e4ff"), .text = hexColor("#001d35") },
.background = .{ .background = hexColor("#fffbff"), .text = hexColor("#2b0052") },
.outline = hexColor("#7c757e"),
};
allegro.setNewBitmapFlags(allegro.NewBitmapFlags{ .MIN_LINEAR = true, .MAG_LINEAR = true });
try context.textures.addFromFile("opaque", assetsDir ++ "/images/opaque.png");
try context.textures.addFromFile("title_untitled", assetsDir ++ "/images/title_untitled.png");
allegro.convertMemoryBitmaps();
try context.fonts.addFromFileTTF("sub", assetsDir ++ "/fonts/Cabin-Regular.ttf", 16);
try context.fonts.addFromFileTTF("default", assetsDir ++ "/fonts/Cabin-Regular.ttf", 32);
try context.switchToScene(TitleScene, null);
var t = allegro.getTime();
while (!context.shouldQuit) {
const scene = if (context.scene) |scene| scene else {
break;
};
while (!context.events.isEmpty()) {
var event: allegro.Event = undefined;
_ = context.events.get(&event);
switch (event.type) {
allegro.c.ALLEGRO_EVENT_DISPLAY_CLOSE => context.quit(),
allegro.c.ALLEGRO_EVENT_DISPLAY_RESIZE => {
context.resized(event.display.width, event.display.height);
_ = allegro.acknowledgeResize(allegro.Display{ .native = event.display.source.? });
},
allegro.c.ALLEGRO_EVENT_KEY_CHAR => {
switch (event.keyboard.keycode) {
allegro.c.ALLEGRO_KEY_ESCAPE => context.quit(),
allegro.c.ALLEGRO_KEY_F11 => context.toggleFullScreen(),
else => {},
}
},
else => {},
}
if (context.shouldQuit) {
break;
}
try scene.handle(&context, &event);
}
const newT = allegro.getTime();
const deltaT = @floatCast(f32, newT - t);
t = newT;
scene.update(&context, deltaT);
scene.render(&context);
allegro.flipDisplay();
}
}
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());
}

36
src/main_menu_scene.zig Normal file
View File

@ -0,0 +1,36 @@
const allegro = @import("allegro");
const Context = @import("context.zig").Context;
pub const MainMenuScene = struct {
pub fn enter(self: *MainMenuScene, ctx: *Context) void {
_ = ctx;
_ = self;
}
pub fn exit(self: *MainMenuScene, ctx: *Context) void {
_ = ctx;
_ = self;
}
pub fn handle(self: *MainMenuScene, ctx: *Context, event: *allegro.Event) !void {
_ = event;
_ = ctx;
_ = self;
}
pub fn update(self: *MainMenuScene, ctx: *Context, dt: f32) void {
_ = self;
_ = dt;
_ = ctx;
}
pub fn render(self: *MainMenuScene, ctx: *Context) void {
_ = self;
allegro.clearToColor(ctx.palette.background.background);
const center = ctx.viewport.center();
ctx.textures.get("title_untitled").?.drawTintedCenteredScaled(ctx.palette.background.text, center.x, ctx.viewport.bounds.min.y + ctx.viewport.scale * 100, 0.5 * ctx.viewport.scale);
allegro.drawText(ctx.fonts.get("default").?, ctx.palette.background.text, center.x, ctx.viewport.bounds.min.y + ctx.viewport.scale * 200, allegro.DrawTextFlags.ALIGN_CENTER, "Play game");
}
};

14
src/palette.zig Normal file
View File

@ -0,0 +1,14 @@
const allegro = @import("allegro");
pub const Palette = struct {
pub const Combination = struct { background: allegro.Color, text: allegro.Color };
primary: Combination,
primaryContainer: Combination,
secondary: Combination,
secondaryContainer: Combination,
tertiary: Combination,
tertiaryContainer: Combination,
background: Combination,
outline: allegro.Color,
};

5
src/scene.zig Normal file
View File

@ -0,0 +1,5 @@
const allegro = @import("allegro");
const engine = @import("engine.zig");
const Context = @import("context.zig").Context;
pub const Scene = engine.Scene(Context, *allegro.Event);

45
src/title_scene.zig Normal file
View File

@ -0,0 +1,45 @@
const allegro = @import("allegro");
const Context = @import("context.zig").Context;
const MainMenuScene = @import("main_menu_scene.zig").MainMenuScene;
pub const TitleScene = struct {
offset: f32,
pub fn enter(self: *TitleScene, ctx: *Context) void {
_ = ctx;
_ = self;
}
pub fn exit(self: *TitleScene, ctx: *Context) void {
_ = ctx;
_ = self;
}
pub fn handle(self: *TitleScene, ctx: *Context, event: *allegro.Event) !void {
switch (event.type) {
allegro.c.ALLEGRO_EVENT_KEY_CHAR => {
switch (event.keyboard.keycode) {
allegro.c.ALLEGRO_KEY_SPACE => try ctx.switchToScene(MainMenuScene, null),
else => {},
}
},
else => {},
}
_ = self;
}
pub fn update(self: *TitleScene, ctx: *Context, dt: f32) void {
_ = self;
_ = dt;
_ = ctx;
}
pub fn render(self: *TitleScene, ctx: *Context) void {
_ = self;
allegro.clearToColor(ctx.palette.background.background);
ctx.textures.get("opaque").?.drawTintedScaled(ctx.palette.tertiaryContainer.background, ctx.viewport.bounds.min.x, ctx.viewport.bounds.min.y, ctx.viewport.bounds.width(), ctx.viewport.bounds.height(), allegro.DrawFlags{});
const center = ctx.viewport.center();
ctx.textures.get("title_untitled").?.drawTintedCenteredScaled(ctx.palette.background.text, center.x, center.y, ctx.viewport.scale);
}
};