Enabled tile randomization.

This commit is contained in:
Sander Schobers 2023-06-04 19:23:37 +02:00
parent e66908ca71
commit 7a51415e5e
6 changed files with 85 additions and 31 deletions

View File

@ -3,6 +3,7 @@ const assets = @import("engine/assets.zig");
pub const Fonts = assets.Fonts; pub const Fonts = assets.Fonts;
pub const FPS = @import("engine/fps.zig").FPS; pub const FPS = @import("engine/fps.zig").FPS;
pub const Keys = @import("engine/keys.zig").Keys; pub const Keys = @import("engine/keys.zig").Keys;
pub const Object = @import("engine/object.zig").Object;
pub const OpaquePtr = @import("engine/opaque_ptr.zig").OpaquePtr; pub const OpaquePtr = @import("engine/opaque_ptr.zig").OpaquePtr;
pub const Point = @import("engine/point.zig").Point; pub const Point = @import("engine/point.zig").Point;
pub const PointF = @import("engine/point_f.zig").PointF; pub const PointF = @import("engine/point_f.zig").PointF;

19
src/engine/object.zig Normal file
View File

@ -0,0 +1,19 @@
const std = @import("std");
pub fn Object(comptime T: type) type {
return struct {
const Self = @This();
allocator: std.mem.Allocator,
ptr: *T,
pub fn create(allocator: std.mem.Allocator) !Self {
const ptr = try allocator.create(T);
return Self{ .allocator = allocator, .ptr = ptr };
}
pub fn destroy(self: Self) void {
self.allocator.destroy(self.ptr);
}
};
}

View File

@ -2,8 +2,9 @@ const std = @import("std");
const allegro = @import("allegro"); const allegro = @import("allegro");
const engine = @import("../engine.zig"); const engine = @import("../engine.zig");
const Animation = @import("animation.zig").Animation; const Animation = @import("animation.zig").Animation;
const Renderer = @import("../renderer.zig").Renderer;
const Level = @import("level.zig").Level; const Level = @import("level.zig").Level;
const Renderer = @import("../renderer.zig").Renderer;
const TileMap = @import("tile_map.zig").TileMap;
pub const Game = struct { pub const Game = struct {
pub const Direction = enum { pub const Direction = enum {
@ -13,6 +14,8 @@ pub const Game = struct {
pub const TileSize = 32; pub const TileSize = 32;
prng: std.rand.DefaultPrng = std.rand.DefaultPrng.init(0),
level: Level, level: Level,
health: i64 = 4, health: i64 = 4,
@ -25,10 +28,11 @@ pub const Game = struct {
starAnimation: Animation, starAnimation: Animation,
renderer: *Renderer, renderer: *Renderer,
randomTile: TileMap(usize),
// current viewport translated to tiles. // current viewport translated to tiles.
boundsInTiles: engine.RectangleF, boundsInTiles: engine.RectangleF,
pub fn init(level: Level, renderer: *Renderer) Game { pub fn init(allocator: std.mem.Allocator, level: Level, renderer: *Renderer) Game {
const playerPosition = level.character.float(); const playerPosition = level.character.float();
return Game{ return Game{
.level = level, .level = level,
@ -39,10 +43,16 @@ pub const Game = struct {
.isPlayerWalking = false, .isPlayerWalking = false,
.starAnimation = Animation.init(renderer.sprites.get("item_star_32").?, 0.15), .starAnimation = Animation.init(renderer.sprites.get("item_star_32").?, 0.15),
.renderer = renderer, .renderer = renderer,
.randomTile = TileMap(usize).init(allocator),
.boundsInTiles = Game.calculateBoundsInTiles(playerPosition), .boundsInTiles = Game.calculateBoundsInTiles(playerPosition),
}; };
} }
pub fn deinit(self: *Game) void {
self.level.deinit();
self.randomTile.deinit();
}
fn calculateBoundsInTiles(position: engine.PointF) engine.RectangleF { fn calculateBoundsInTiles(position: engine.PointF) engine.RectangleF {
return engine.RectangleF.initRelative( return engine.RectangleF.initRelative(
position.x - 15, position.x - 15,
@ -77,6 +87,15 @@ pub const Game = struct {
self.boundsInTiles = Game.calculateBoundsInTiles(self.playerPosition); self.boundsInTiles = Game.calculateBoundsInTiles(self.playerPosition);
} }
pub fn randomTileOffset(self: *Game, x: i64, y: i64, maxOffset: usize) usize {
if (self.randomTile.get(x, y)) |offset| {
return offset;
}
const offset = self.prng.random().intRangeLessThan(usize, 0, maxOffset);
self.randomTile.set(x, y, offset) catch {};
return offset;
}
// return values are in view coordinates // return values are in view coordinates
pub fn tile(self: Game, x: f32, y: f32) engine.PointF { pub fn tile(self: Game, x: f32, y: f32) engine.PointF {
return self.tileP(engine.PointF.init(x, y)); return self.tileP(engine.PointF.init(x, y));

View File

@ -15,7 +15,7 @@ pub const Level = struct {
tiles: TileMap(Tile), tiles: TileMap(Tile),
collectables: TileMap(Collectable), collectables: TileMap(Collectable),
pub fn load(allocator: std.mem.Allocator, path: []const u8) !Level { pub fn init(allocator: std.mem.Allocator, path: []const u8) !Level {
const path_ = try std.fs.realpathAlloc(allocator, path); const path_ = try std.fs.realpathAlloc(allocator, path);
defer allocator.free(path_); defer allocator.free(path_);
@ -25,10 +25,10 @@ pub const Level = struct {
const data = try file.readToEndAlloc(allocator, 1024 * 1024); const data = try file.readToEndAlloc(allocator, 1024 * 1024);
defer allocator.free(data); defer allocator.free(data);
return try Level.loadFromMemory(allocator, data); return try Level.initFromMemory(allocator, data);
} }
pub fn loadFromMemory(allocator: std.mem.Allocator, data: []const u8) !Level { pub fn initFromMemory(allocator: std.mem.Allocator, data: []const u8) !Level {
const n = std.mem.count(u8, data, "\n"); const n = std.mem.count(u8, data, "\n");
_ = n; _ = n;
@ -41,9 +41,6 @@ pub const Level = struct {
while (lines.next()) |line| { while (lines.next()) |line| {
defer y += 1; defer y += 1;
try tiles.ensureColumns(line.len);
try collectables.ensureColumns(line.len);
for (line, 0..) |tile, column| { for (line, 0..) |tile, column| {
var x = @intCast(i64, column); var x = @intCast(i64, column);
switch (tile) { switch (tile) {
@ -61,4 +58,9 @@ pub const Level = struct {
.collectables = collectables, .collectables = collectables,
}; };
} }
pub fn deinit(self: *Level) void {
self.tiles.deinit();
self.collectables.deinit();
}
}; };

View File

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const Object = @import("../engine/object.zig").Object;
pub fn TileMap(comptime Value: type) type { pub fn TileMap(comptime Value: type) type {
return struct { return struct {
@ -13,7 +14,7 @@ pub fn TileMap(comptime Value: type) type {
}; };
} }
pub fn deinit(self: Column) void { pub fn deinit(self: *Column) void {
self.values.deinit(); self.values.deinit();
} }
@ -27,35 +28,45 @@ pub fn TileMap(comptime Value: type) type {
}; };
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
columns: std.ArrayList(Column), columns: std.AutoHashMap(i64, Column),
pub fn init(allocator: std.mem.Allocator) Self { pub fn init(allocator: std.mem.Allocator) Self {
return Self{ return Self{
.allocator = allocator, .allocator = allocator,
.columns = std.ArrayList(Column).init(allocator), .columns = std.AutoHashMap(i64, Column).init(allocator),
}; };
} }
pub fn deinit(self: Self) void { pub fn deinit(self: *Self) void {
for (self.columns.items) |item| { var columns = self.columns.valueIterator();
item.deinit(); while (columns.next()) |c| {
c.deinit();
} }
self.columns.deinit(); self.columns.deinit();
} }
pub fn column(self: Self, i: i64) *Column { pub fn column(self: Self, x: i64) *Column {
return &self.columns.items[@intCast(usize, i)]; return self.columns.getPtr(x).?;
} }
pub fn ensureColumns(self: *Self, n: usize) !void { pub fn ensureColumn(self: *Self, x: i64) !*Column {
while (self.columns.items.len < n) { if (self.columns.getPtr(x)) |c| {
try self.columns.append(Column.init(self.allocator)); return c;
} }
try self.columns.put(x, Column.init(self.allocator));
return self.columns.getPtr(x).?;
} }
pub fn set(self: *Self, c: i64, r: i64, value: Value) !void { pub fn get(self: Self, x: i64, y: i64) ?Value {
try self.ensureColumns(@intCast(usize, c) + 1); if (self.columns.get(x)) |c| {
try self.column(c).set(r, value); return c.get(y);
}
return null;
}
pub fn set(self: *Self, x: i64, y: i64, value: Value) !void {
const c = try self.ensureColumn(x);
try c.set(y, value);
} }
}; };
} }

View File

@ -10,13 +10,13 @@ pub const GameScene = struct {
game: game.Game = undefined, game: game.Game = undefined,
pub fn enter(self: *GameScene, ctx: *Context) void { pub fn enter(self: *GameScene, ctx: *Context) void {
const level = game.Level.load(ctx.allocator, paths.AssetsDir ++ "/levels/level1.txt") catch unreachable; const level = game.Level.init(ctx.allocator, paths.AssetsDir ++ "/levels/level1.txt") catch unreachable;
self.game = game.Game.init(level, &ctx.renderer); self.game = game.Game.init(ctx.allocator, level, &ctx.renderer);
} }
pub fn exit(self: *GameScene, ctx: *Context) void { pub fn exit(self: *GameScene, ctx: *Context) void {
self.game.deinit();
_ = ctx; _ = ctx;
_ = self;
} }
pub fn handle(self: *GameScene, ctx: *Context, event: *allegro.Event) !void { pub fn handle(self: *GameScene, ctx: *Context, event: *allegro.Event) !void {
@ -68,20 +68,22 @@ pub const GameScene = struct {
var x = tileBounds.min.x; var x = tileBounds.min.x;
while (x < tileBounds.max.x) : (x += 1) { while (x < tileBounds.max.x) : (x += 1) {
self.game.drawSpriteFrame("tiles_dirt_32", 0, x, 19); const randomDirtOffset = self.game.randomTileOffset(x, 99, 3);
self.game.drawSpriteFrame("tiles_dirt_32", 3, x, 20); self.game.drawSpriteFrame("tiles_dirt_32", 0 + randomDirtOffset, x, 19);
self.game.drawSpriteFrame("tiles_dirt_32", 3 + randomDirtOffset, x, 20);
if (x < 0) continue; if (x < 0) continue;
const tiles = self.game.level.tiles.column(x); const tiles = self.game.level.tiles.ensureColumn(x) catch continue;
const collectables = self.game.level.collectables.column(x); const collectables = self.game.level.collectables.ensureColumn(x) catch continue;
var y = tileBounds.min.y; var y = tileBounds.min.y;
while (y < tileBounds.max.y) : (y += 1) { while (y < tileBounds.max.y) : (y += 1) {
if (tiles.get(y)) |tile| { if (tiles.get(y)) |tile| {
switch (tile) { switch (tile) {
game.Level.Tile.Grass => { game.Level.Tile.Grass => {
self.game.drawSpriteFrame("tiles_grass_32", 1, x, y - 1); const randomOffset = self.game.randomTileOffset(x, y, 2);
self.game.drawSpriteFrame("tiles_grass_32", 5, x, y); self.game.drawSpriteFrame("tiles_grass_32", 1 + randomOffset, x, y - 1);
self.game.drawSpriteFrame("tiles_grass_32", 5 + randomOffset, x, y);
}, },
// else => {}, // else => {},
} }