add moveable pots
This commit is contained in:
parent
e5e4e429b6
commit
73fbbafbfc
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="20" height="15" tilewidth="8" tileheight="8" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="20" height="15" tilewidth="8" tileheight="8" infinite="0" nextlayerid="3" nextobjectid="1">
|
||||
<tileset firstgid="1" source="tileset.tsx"/>
|
||||
<layer id="1" name="Tile Layer 1" width="20" height="15">
|
||||
<layer id="1" name="Actors" width="20" height="15">
|
||||
<data encoding="csv">
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
@ -9,7 +9,7 @@
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,5,0,0,0,41,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
@ -18,6 +18,28 @@
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
</data>
|
||||
</layer>
|
||||
<layer id="2" name="Walls" width="20" height="15">
|
||||
<properties>
|
||||
<property name="solid" type="bool" value="true"/>
|
||||
</properties>
|
||||
<data encoding="csv">
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,1,2,2,2,2,2,2,2,2,4,0,0,0,0,0,0,
|
||||
0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,
|
||||
0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,
|
||||
0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,
|
||||
0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,
|
||||
0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,
|
||||
0,0,0,0,17,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,
|
||||
0,0,0,0,83,2,2,68,2,2,2,37,67,36,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
"recentFiles": [
|
||||
"first.tmx"
|
||||
],
|
||||
"textEdit.monospace": true,
|
||||
"tileset.lastUsedFormat": "tsx",
|
||||
"tileset.tileSize": {
|
||||
"height": 8,
|
||||
|
||||
@ -1,4 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.10" tiledversion="1.11.2" name="tileset" tilewidth="8" tileheight="8" tilecount="160" columns="16">
|
||||
<image source="../kenney-micro-roguelike/colored_tilemap_packed.png" width="128" height="80"/>
|
||||
<tile id="4">
|
||||
<properties>
|
||||
<property name="type" value="player"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="40">
|
||||
<properties>
|
||||
<property name="type" value="pot"/>
|
||||
</properties>
|
||||
</tile>
|
||||
</tileset>
|
||||
|
||||
@ -12,7 +12,9 @@ pub const Id = List.Id;
|
||||
|
||||
pub const Type = enum {
|
||||
nil,
|
||||
player
|
||||
player,
|
||||
solid,
|
||||
pot,
|
||||
};
|
||||
|
||||
type: Type,
|
||||
|
||||
247
src/game.zig
247
src/game.zig
@ -25,34 +25,58 @@ pub const Input = struct {
|
||||
move_down: Window.KeyState,
|
||||
move_left: Window.KeyState,
|
||||
move_right: Window.KeyState,
|
||||
restart: bool
|
||||
};
|
||||
|
||||
const tile_size = Vec2.init(8, 8);
|
||||
|
||||
canvas_size: Vec2,
|
||||
pub const Level = struct {
|
||||
entities: Entity.List,
|
||||
timers: Timer.List,
|
||||
|
||||
last_move: Vec2,
|
||||
pub const empty = Level{
|
||||
.entities = .empty,
|
||||
.timers = .empty
|
||||
};
|
||||
|
||||
pub fn clone(self: *Level, gpa: Allocator) !Level {
|
||||
var entities = try self.entities.clone(gpa);
|
||||
errdefer entities.deinit(gpa);
|
||||
|
||||
var timers = try self.timers.clone(gpa);
|
||||
errdefer timers.deinit(gpa);
|
||||
|
||||
return Level{
|
||||
.entities = entities,
|
||||
.timers = timers
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Level, gpa: Allocator) void {
|
||||
self.entities.deinit(gpa);
|
||||
self.timers.deinit(gpa);
|
||||
}
|
||||
};
|
||||
|
||||
canvas_size: Vec2,
|
||||
level: Level,
|
||||
|
||||
current_level: u32,
|
||||
levels: std.ArrayList(Level),
|
||||
|
||||
last_up_repeat_at: ?f64 = null,
|
||||
last_down_repeat_at: ?f64 = null,
|
||||
last_left_repeat_at: ?f64 = null,
|
||||
last_right_repeat_at: ?f64 = null,
|
||||
|
||||
pub fn init(gpa: Allocator) !Game {
|
||||
var game = Game{
|
||||
.canvas_size = (Vec2.init(20, 15)).multiply(tile_size),
|
||||
.entities = .empty,
|
||||
.timers = .empty,
|
||||
.last_move = .init(0, 0)
|
||||
};
|
||||
errdefer game.deinit(gpa);
|
||||
show_grid: bool = false,
|
||||
|
||||
_ = try game.entities.insert(gpa, .{
|
||||
.type = .player,
|
||||
.position = .init(0, 0),
|
||||
.render_tile = .{ .id = .player },
|
||||
});
|
||||
pub fn init(gpa: Allocator) !Game {
|
||||
var self = Game{
|
||||
.canvas_size = (Vec2.init(20, 15)),
|
||||
.level = .empty,
|
||||
.levels = .empty,
|
||||
.current_level = 0,
|
||||
};
|
||||
errdefer self.deinit(gpa);
|
||||
|
||||
const manager = try tiled.ResourceManager.init();
|
||||
defer manager.deinit();
|
||||
@ -62,6 +86,24 @@ pub fn init(gpa: Allocator) !Game {
|
||||
const map = try manager.loadMapFromBuffer(@embedFile("assets/tiled/first.tmx"));
|
||||
defer map.deinit();
|
||||
|
||||
try self.levels.append(gpa, try loadLevelFromTiled(gpa, map));
|
||||
try self.restartLevel(gpa);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn restartLevel(self: *Game, gpa: Allocator) !void {
|
||||
const level_copy = try self.levels.items[self.current_level].clone(gpa);
|
||||
errdefer level_copy.deinit(gpa);
|
||||
|
||||
self.level.deinit(gpa);
|
||||
self.level = level_copy;
|
||||
}
|
||||
|
||||
fn loadLevelFromTiled(gpa: Allocator, map: tiled.Map) !Level {
|
||||
var level: Level = .empty;
|
||||
errdefer level.deinit(gpa);
|
||||
|
||||
var layer_iter = map.iterLayers();
|
||||
while (layer_iter.next()) |layer| {
|
||||
if (layer.layer.visible == 0) {
|
||||
@ -71,31 +113,127 @@ pub fn init(gpa: Allocator) !Game {
|
||||
continue;
|
||||
}
|
||||
|
||||
const layer_props = tiled.Properties{ .inner = layer.layer.properties };
|
||||
const is_layer_solid = layer_props.getPropertyBool("solid") orelse false;
|
||||
|
||||
const map_width = map.map.width;
|
||||
for (0..map.map.height) |y| {
|
||||
for (0..map_width) |x| {
|
||||
|
||||
const tile = map.getTile(layer, x, y) orelse continue;
|
||||
const tile_props = tiled.Properties{ .inner = tile.tile.properties };
|
||||
const tile_type: []const u8 = std.mem.span(tile_props.getPropertyString("type") orelse "");
|
||||
|
||||
if (tile.getPropertyString("type")) |tile_type| {
|
||||
_ = tile_type; // autofix
|
||||
}
|
||||
|
||||
_ = try game.entities.insert(gpa, .{
|
||||
const tile_size = Vec2.init(8, 8);
|
||||
var entity: Entity = .{
|
||||
.type = .nil,
|
||||
.position = Vec2.init(@floatFromInt(x), @floatFromInt(y)).multiply(tile_size),
|
||||
.position = Vec2.init(@floatFromInt(x), @floatFromInt(y)),
|
||||
.render_tile = .{ .position = tile.getUpperLeft().divide(tile_size) },
|
||||
});
|
||||
};
|
||||
if (std.mem.eql(u8, tile_type, "player")) {
|
||||
entity.type = .player;
|
||||
} else if (std.mem.eql(u8, tile_type, "pot")) {
|
||||
entity.type = .pot;
|
||||
} else if (is_layer_solid) {
|
||||
entity.type = .solid;
|
||||
}
|
||||
|
||||
_ = try level.entities.insert(gpa, entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return game;
|
||||
return level;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Game, gpa: Allocator) void {
|
||||
self.entities.deinit(gpa);
|
||||
self.timers.deinit(gpa);
|
||||
self.level.deinit(gpa);
|
||||
for (self.levels.items) |*level| {
|
||||
level.deinit(gpa);
|
||||
}
|
||||
self.levels.deinit(gpa);
|
||||
}
|
||||
|
||||
fn drawGrid(self: *Game, size: Vec2, color: Vec4, line_width: f32) void {
|
||||
var x: f32 = 0;
|
||||
while (x < self.canvas_size.x) {
|
||||
x += size.x;
|
||||
Gfx.drawLine(
|
||||
.init(x, 0),
|
||||
.init(x, self.canvas_size.y),
|
||||
color,
|
||||
line_width
|
||||
);
|
||||
}
|
||||
|
||||
var y: f32 = 0;
|
||||
while (y < self.canvas_size.y) {
|
||||
y += size.y;
|
||||
Gfx.drawLine(
|
||||
.init(0, y),
|
||||
.init(self.canvas_size.x, y),
|
||||
color,
|
||||
line_width
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn getEntityAt(self: *Game, pos: Vec2) ?Entity.Id {
|
||||
var iter = self.level.entities.iterator();
|
||||
while (iter.next()) |tuple| {
|
||||
const entity = tuple.item;
|
||||
if (entity.position.eql(pos)) {
|
||||
return tuple.id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn isSolidAt(self: *Game, pos: Vec2) bool {
|
||||
if (self.getEntityAt(pos)) |entity_id| {
|
||||
const entity = self.level.entities.getAssumeExists(entity_id);
|
||||
return entity.type == .solid;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn canMove(self: *Game, entity: *Entity, dir: Vec2) bool {
|
||||
const next_pos = entity.position.add(dir);
|
||||
if (self.isSolidAt(next_pos)) {
|
||||
return false;
|
||||
}
|
||||
if (next_pos.x < 0 or next_pos.x >= self.canvas_size.x) {
|
||||
return false;
|
||||
}
|
||||
if (next_pos.y < 0 or next_pos.y >= self.canvas_size.y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn moveEntity(self: *Game, entity: *Entity, dir: Vec2) bool {
|
||||
if (entity.type == .solid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dir.x == 0 and dir.y == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const next_pos = entity.position.add(dir);
|
||||
if (self.getEntityAt(next_pos)) |next_entity_id| {
|
||||
const next_entity = self.level.entities.getAssumeExists(next_entity_id);
|
||||
if (!self.moveEntity(next_entity, dir)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
entity.position = next_pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn getInput(self: *Game, window: *Window) Input {
|
||||
@ -109,45 +247,27 @@ pub fn getInput(self: *Game, window: *Window) Input {
|
||||
.move_down = window.getKeyState(.S),
|
||||
.move_left = window.getKeyState(.A),
|
||||
.move_right = window.getKeyState(.D),
|
||||
.restart = window.isKeyPressed(.R)
|
||||
};
|
||||
}
|
||||
|
||||
fn drawGrid(self: *Game, size: Vec2, color: Vec4) void {
|
||||
var x: f32 = 0;
|
||||
while (x < self.canvas_size.x) {
|
||||
x += size.x;
|
||||
Gfx.drawLine(
|
||||
.init(x, 0),
|
||||
.init(x, self.canvas_size.y),
|
||||
color,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
var y: f32 = 0;
|
||||
while (y < self.canvas_size.y) {
|
||||
y += size.y;
|
||||
Gfx.drawLine(
|
||||
.init(0, y),
|
||||
.init(self.canvas_size.x, y),
|
||||
color,
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(self: *Game, input: Input) !void {
|
||||
Gfx.drawRectangle(.init(0, 0), .init(self.canvas_size.x, self.canvas_size.y), rgb_hex("#222323").?);
|
||||
self.drawGrid(tile_size, rgb(20, 20, 20));
|
||||
if (self.show_grid) {
|
||||
self.drawGrid(.init(1, 1), rgb(20, 20, 20), 0.1);
|
||||
}
|
||||
|
||||
self.timers.now += input.dt;
|
||||
if (input.restart) {
|
||||
try self.restartLevel(input.allocator);
|
||||
}
|
||||
|
||||
self.level.timers.now += input.dt;
|
||||
|
||||
var move: Vec2 = .init(0, 0);
|
||||
defer self.last_move = move;
|
||||
|
||||
const repeat_options = Window.KeyState.RepeatOptions{
|
||||
.first_at = 0.4,
|
||||
.period = 0.2
|
||||
.first_at = 0.3,
|
||||
.period = 0.1
|
||||
};
|
||||
|
||||
if (input.move_up.pressed or input.move_up.repeat(&self.last_up_repeat_at, repeat_options)) {
|
||||
@ -163,18 +283,16 @@ pub fn tick(self: *Game, input: Input) !void {
|
||||
move.x += 1;
|
||||
}
|
||||
|
||||
var iter = self.entities.iterator();
|
||||
var iter = self.level.entities.iterator();
|
||||
while (iter.nextItem()) |entity| {
|
||||
if (entity.type == .player) {
|
||||
// const velocity = input.move.multiplyScalar(100);
|
||||
// entity.position = entity.position.add(velocity.multiplyScalar(input.dt));
|
||||
entity.position = entity.position.add(move.multiply(tile_size));
|
||||
_ = self.moveEntity(entity, move);
|
||||
}
|
||||
|
||||
if (entity.render_tile) |render_tile| {
|
||||
switch (render_tile) {
|
||||
.id => |tile_id| Gfx.drawTileById(tile_id, entity.position, tile_size, rgb(255, 255, 255)),
|
||||
.position => |position| Gfx.drawTile(position, entity.position, tile_size, rgb(255, 255, 255)),
|
||||
.id => |tile_id| Gfx.drawTileById(tile_id, entity.position, .init(1,1), rgb(255, 255, 255)),
|
||||
.position => |position| Gfx.drawTile(position, entity.position, .init(1,1), rgb(255, 255, 255)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -190,6 +308,7 @@ pub fn debug(self: *Game) !void {
|
||||
}
|
||||
defer imgui.endWindow();
|
||||
|
||||
imgui.textFmt("Entities: {}", .{self.entities.len});
|
||||
imgui.textFmt("Timers: {}", .{self.timers.array_list.len});
|
||||
imgui.textFmt("Entities: {}", .{self.level.entities.len});
|
||||
imgui.textFmt("Timers: {}", .{self.level.timers.array_list.len});
|
||||
_ = imgui.checkbox("Show grid", &self.show_grid);
|
||||
}
|
||||
|
||||
@ -300,6 +300,26 @@ pub fn GenerationalArrayList(Item: type) type {
|
||||
allocator.free(self.items[0..self.capacity]);
|
||||
}
|
||||
|
||||
pub fn clone(self: *Self, allocator: Allocator) !Self {
|
||||
const items = try allocator.dupe(Item, self.items[0..self.capacity]);
|
||||
errdefer allocator.free(items);
|
||||
|
||||
const generations = try allocator.dupe(Generation, self.generations[0..self.capacity]);
|
||||
errdefer allocator.free(generations);
|
||||
|
||||
const unused = try allocator.dupe(u8, self.unused[0..divCeilGeneration(self.capacity)]);
|
||||
errdefer allocator.free(unused);
|
||||
|
||||
return Self{
|
||||
.items = items.ptr,
|
||||
.generations = generations.ptr,
|
||||
.unused = unused.ptr,
|
||||
.len = self.len,
|
||||
.count = self.count,
|
||||
.capacity = self.capacity
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getMetadata(self: *Self) Metadata {
|
||||
return Metadata{
|
||||
.len = self.len,
|
||||
|
||||
@ -63,18 +63,6 @@ pub const ResourceManager = struct {
|
||||
pub const Tile = struct {
|
||||
tile: *c.tmx_tile,
|
||||
|
||||
pub fn getPropertyString(self: Tile, key: [*:0]const u8) ?[*:0]const u8 {
|
||||
const maybe_prop = c.tmx_get_property(self.tile.properties, key);
|
||||
if (maybe_prop == null) {
|
||||
return null;
|
||||
}
|
||||
const prop: *c.tmx_property = maybe_prop.?;
|
||||
if (prop.type != c.PT_STRING) {
|
||||
return null;
|
||||
}
|
||||
return prop.value.string;
|
||||
}
|
||||
|
||||
pub fn getUpperLeft(self: Tile) Vec2 {
|
||||
return Vec2.init(
|
||||
@floatFromInt(self.tile.ul_x),
|
||||
@ -152,4 +140,38 @@ pub const Layer = struct {
|
||||
};
|
||||
};
|
||||
|
||||
pub const Properties = struct {
|
||||
inner: ?*c.tmx_properties,
|
||||
|
||||
pub fn getPropertyString(self: Properties, key: [*:0]const u8) ?[*:0]const u8 {
|
||||
const inner = self.inner orelse return null;
|
||||
|
||||
const maybe_prop = c.tmx_get_property(inner, key);
|
||||
if (maybe_prop == null) {
|
||||
return null;
|
||||
}
|
||||
const prop: *c.tmx_property = maybe_prop.?;
|
||||
if (prop.type != c.PT_STRING) {
|
||||
return null;
|
||||
}
|
||||
return prop.value.string;
|
||||
}
|
||||
|
||||
pub fn getPropertyBool(self: Properties, key: [*:0]const u8) ?bool {
|
||||
const inner = self.inner orelse return null;
|
||||
|
||||
const maybe_prop = c.tmx_get_property(inner, key);
|
||||
if (maybe_prop == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const prop: *c.tmx_property = maybe_prop.?;
|
||||
if (prop.type != c.PT_BOOL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return prop.value.boolean != 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const FLIP_BITS_REMOVAL: u32 = c.TMX_FLIP_BITS_REMOVAL;
|
||||
|
||||
@ -32,6 +32,16 @@ pub const List = struct {
|
||||
self.array_list.deinit(gpa);
|
||||
}
|
||||
|
||||
pub fn clone(self: *List, gpa: Allocator) !List {
|
||||
const array_list = try self.array_list.clone(gpa);
|
||||
errdefer array_list.deinit(gpa);
|
||||
|
||||
return List{
|
||||
.now = self.now,
|
||||
.array_list = array_list
|
||||
};
|
||||
}
|
||||
|
||||
pub fn start(self: *List, gpa: Allocator, opts: Options) !Id {
|
||||
assert(opts.duration > 0);
|
||||
return try self.array_list.insert(gpa, .{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user