add terrain

This commit is contained in:
Rokas Puzonas 2026-07-18 23:50:32 +03:00
parent f94ae22214
commit 892fbb38be
3 changed files with 103 additions and 9 deletions

View File

@ -11,11 +11,8 @@ pub fn build(b: *std.Build) !void {
"Prototype_Character/Default/idle.png",
"Prototype_Character/Default/walk.png",
"Prototype_Character/Default/death.png",
"font/pixeloid/PixeloidSans.ttf"
// "sokoban/sokoban_tilesheet.png",
// "roboto-font/Roboto-Regular.ttf",
// "tiled/map.tmx",
// "tiled/tileset.tsx"
"font/pixeloid/PixeloidSans.ttf",
"tranquil_tunnels_transparent.png"
};
const asset_dir_realpath = try b.build_root.join(b.graph.arena, &.{ assets_dir });

View File

@ -44,6 +44,14 @@ idle_animations: AnimationDirections,
walk_animations: AnimationDirections,
death_animations: AnimationDirections,
terrain_sheet: Tilesheet,
terrain_tiles: []Tile,
const terrain_origin_x: i32 = -80;
const terrain_origin_y: i32 = -60;
const terrain_width = 160;
const terrain_height = 70;
const EntitySlotMap = SlotMapType(u16, u16, Entity);
const EntityId = EntitySlotMap.Id;
@ -219,7 +227,7 @@ const Entity = struct {
};
const Upgrades = struct {
minion_count: u32 = 0,
minion_count: u32 = 1,
minion_chop_damage: u32 = 2,
minion_chop_rate: f32 = 1,
minion_throw_cooldown: Nanoseconds = std.time.ns_per_s,
@ -227,7 +235,7 @@ const Upgrades = struct {
minion_carry_capacity: u32 = 1,
minion_walk: MinionWalkStats = .{},
tree_station_count: u32 = 0,
tree_station_count: u32 = 1,
tree_level: u32 = 1,
tree_health: u32 = tree_levels[0].health,
tree_respawn_duration: Nanoseconds = tree_levels[0].respawn_duration,
@ -390,6 +398,10 @@ const Shop = struct {
};
};
const Tile = struct {
sprite: Gfx.Sprite.Id
};
fn initImageDataFromSTB(img: STBImage) Gfx.ImageData {
return Gfx.ImageData{
.width = img.width,
@ -429,6 +441,16 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 {
.init(32, 32)
);
const terrain_sheet = try Tilesheet.init(
plt.arena,
initImageDataFromSTB(try STBImage.load(plt.assets.readFile("tranquil_tunnels_transparent.png"))),
128,
128,
.init(8, 8)
);
const terrain_tiles = try plt.arena.alloc(Tile, terrain_width*terrain_height);
const ns_per_s = std.time.ns_per_s;
const idle_fps = 5;
const walk_fps = 5;
@ -449,9 +471,13 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 {
.eternal_flame_id = undefined,
.upgrades = .{},
.shop = undefined,
.terrain_sheet = terrain_sheet,
.terrain_tiles = terrain_tiles,
};
try self.initShop(plt.arena);
self.initTerrain();
self.spawnBurnStation(100);
self.spawnBurnStation(150);
@ -553,6 +579,62 @@ fn initShop(self: *App, arena: Allocator) !void {
};
}
fn pickRandom(self: *App, T: type, sprites: []const T) T {
const rng = self.prng.random();
return sprites[rng.uintLessThan(usize, sprites.len)];
}
fn initTerrain(self: *App) void {
@memset(self.terrain_tiles, Tile{
.sprite = Gfx.getNilSprite()
});
const surface_tiles = &[_]Gfx.Sprite.Id{
self.terrain_sheet.get(4, 23),
self.terrain_sheet.get(5, 23),
self.terrain_sheet.get(3, 23),
self.terrain_sheet.get(4, 24)
};
for (0..terrain_width) |x| {
const tile = self.getTerrainTile(@as(i32, @intCast(x)) + terrain_origin_x, 0);
tile.sprite = self.pickRandom(Gfx.Sprite.Id, surface_tiles);
}
const underground_tiles = &[_]Gfx.Sprite.Id{
self.terrain_sheet.get(11, 23),
self.terrain_sheet.get(12, 23),
self.terrain_sheet.get(11, 24),
self.terrain_sheet.get(12, 24),
self.terrain_sheet.get(3, 107),
self.terrain_sheet.get(3, 107),
self.terrain_sheet.get(3, 107),
};
for (0..terrain_width) |x| {
for (1..4) |y| {
const tile = self.getTerrainTile(@as(i32, @intCast(x)) + terrain_origin_x, @intCast(y));
tile.sprite = self.pickRandom(Gfx.Sprite.Id, underground_tiles);
}
}
}
fn getTerrainTile(self: *App, x: i32, y: i32) *Tile {
const left_edge = terrain_origin_x;
const right_edge = terrain_width + terrain_origin_x - 1;
const top_edge = terrain_origin_y;
const bottom_edge = terrain_height + terrain_origin_y - 1;
assert(left_edge <= x and x <= right_edge);
assert(top_edge <= y and y <= bottom_edge);
const x_u32: u32 = @intCast(x-terrain_origin_x);
const y_u32: u32 = @intCast(y-terrain_origin_y);
return &self.terrain_tiles[y_u32 * terrain_width + x_u32];
}
// fn getTerrainEdgeLeft(self: *App)
fn spawnEntity(self: *App, e: Entity) ?EntityId {
return self.entities.insert(e) catch {
log.warn("Failed to spawn entity {}, entity limit reached", .{e.type});
@ -1153,7 +1235,7 @@ fn showShopPanel(
}
Gfx.drawText(self.font, .{
.text = try std.fmt.allocPrint(plt.arena, "{}H", .{cost}),
.text = if (limit_reached) "MAX" else try std.fmt.allocPrint(plt.arena, "{}H", .{cost}),
.pos = .init(panel_size.x - 20, 0),
.height = 24,
.color = text_color,
@ -1605,6 +1687,21 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
}
}
{ // Draw terrain
for (0..terrain_height) |y| {
for (0..terrain_width) |x| {
const tile = self.terrain_tiles[y * terrain_width + x];
if (tile.sprite == Gfx.getNilSprite()) {
continue;
}
const tile_size = Vec2.init(32, 32);
const tile_pos = Vec2.initFromInt(usize, x, y).add(.initFromInt(i32, terrain_origin_x, terrain_origin_y));
Gfx.drawSprite(tile.sprite, tile_pos.multiply(tile_size), tile_size, .white);
}
}
}
{
var tree_iter = self.initEntityIteratorByType(.tree);
while (tree_iter.next()) |id| {

View File

@ -47,7 +47,7 @@ pub fn isMousePressed(self: Input, button: MouseButton) bool {
return self.mouse_buttons.pressed.contains(button);
}
pub fn iMouseReleased(self: Input, button: MouseButton) bool {
pub fn isMouseReleased(self: Input, button: MouseButton) bool {
return self.mouse_buttons.released.contains(button);
}