implement wood burning
This commit is contained in:
parent
0cf55b3ef1
commit
46ca2e5e1e
531
src/app.zig
531
src/app.zig
@ -24,14 +24,17 @@ const App = @This();
|
|||||||
|
|
||||||
const Prng = std.Random.DefaultPrng;
|
const Prng = std.Random.DefaultPrng;
|
||||||
|
|
||||||
|
const max_entities = 2048;
|
||||||
|
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
camera_pos: Vec2,
|
camera_pos: Vec2,
|
||||||
prng: Prng,
|
prng: Prng,
|
||||||
|
|
||||||
heat: u64 = 0,
|
heat: u64 = 0,
|
||||||
|
|
||||||
entities_buffer: [2048]EntitySlotMap.Slot,
|
entities_buffer: [max_entities]EntitySlotMap.Slot,
|
||||||
entities: EntitySlotMap,
|
entities: EntitySlotMap,
|
||||||
|
eternal_flame_id: EntityId,
|
||||||
|
|
||||||
font: Gfx.Font.Id,
|
font: Gfx.Font.Id,
|
||||||
idle_animations: AnimationDirections,
|
idle_animations: AnimationDirections,
|
||||||
@ -155,21 +158,22 @@ const Entity = struct {
|
|||||||
target_entity: ?EntityId = null,
|
target_entity: ?EntityId = null,
|
||||||
targeted_by_entity: ?EntityId = null,
|
targeted_by_entity: ?EntityId = null,
|
||||||
|
|
||||||
minion_state: MinionState = .goto_tree_station,
|
minion_state: MinionState = .wander,
|
||||||
minion_work_progress: f32 = 0,
|
|
||||||
minion_chop_cooldown: Nanoseconds = 0,
|
minion_chop_cooldown: Nanoseconds = 0,
|
||||||
minion_chop_damage: u32 = 2,
|
minion_chop_damage: u32 = 2,
|
||||||
minion_burn_speed: f32 = 1,
|
minion_throw_cooldown: Nanoseconds = 0,
|
||||||
minion_carried_wood: ?EntityId = null,
|
minion_carried_wood: ?EntityId = null,
|
||||||
|
|
||||||
tree_health: u32 = 0,
|
tree_health: u32 = 0,
|
||||||
tree_max_health: u32 = 0,
|
tree_max_health: u32 = 0,
|
||||||
|
|
||||||
station_subtype: StationSubtype = .chop,
|
|
||||||
station_spawn_tree_cooldown: Nanoseconds = 0,
|
station_spawn_tree_cooldown: Nanoseconds = 0,
|
||||||
station_owned_tree: ?EntityId = null,
|
station_owned_tree: ?EntityId = null,
|
||||||
|
|
||||||
wood_carried_by: ?EntityId = null,
|
wood_carried_by: ?EntityId = null,
|
||||||
|
wood_burning: bool = false,
|
||||||
|
wood_health: f32 = 0,
|
||||||
|
wood_max_health: f32 = 0,
|
||||||
|
|
||||||
pos: Vec2 = .init(0, 0),
|
pos: Vec2 = .init(0, 0),
|
||||||
vel: Vec2 = .init(0, 0),
|
vel: Vec2 = .init(0, 0),
|
||||||
@ -184,23 +188,25 @@ const Entity = struct {
|
|||||||
animation_timer: Nanoseconds = 0,
|
animation_timer: Nanoseconds = 0,
|
||||||
|
|
||||||
const MinionState = enum {
|
const MinionState = enum {
|
||||||
|
wander,
|
||||||
|
|
||||||
goto_tree_station,
|
goto_tree_station,
|
||||||
chop_tree,
|
chop_tree,
|
||||||
|
|
||||||
pickup_wood,
|
pickup_wood,
|
||||||
burn
|
|
||||||
|
goto_burn_station,
|
||||||
|
burn_wood
|
||||||
};
|
};
|
||||||
|
|
||||||
const Type = enum {
|
const Type = enum {
|
||||||
nil,
|
nil,
|
||||||
minion,
|
minion,
|
||||||
station,
|
wood_station,
|
||||||
|
burn_station,
|
||||||
tree,
|
tree,
|
||||||
wood,
|
wood,
|
||||||
};
|
eternal_flame,
|
||||||
|
|
||||||
const StationSubtype = enum {
|
|
||||||
chop,
|
|
||||||
burn
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Flags = packed struct {
|
const Flags = packed struct {
|
||||||
@ -264,22 +270,35 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 {
|
|||||||
.entities = .init(&self.entities_buffer),
|
.entities = .init(&self.entities_buffer),
|
||||||
.prng = Prng.init(@truncate(@as(u96, @intCast(now.nanoseconds)))),
|
.prng = Prng.init(@truncate(@as(u96, @intCast(now.nanoseconds)))),
|
||||||
.camera_pos = .init(0, 0),
|
.camera_pos = .init(0, 0),
|
||||||
.font = font
|
.font = font,
|
||||||
|
.eternal_flame_id = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.spawnMinion(.init(0, 0));
|
self.spawnMinion(.init(0, 0));
|
||||||
// try self.spawnMinion(.init(32, 0));
|
// try self.spawnMinion(.init(32, 0));
|
||||||
|
|
||||||
self.spawnStation(-100, .chop);
|
self.spawnWoodStation(-100);
|
||||||
self.spawnStation(-150, .chop);
|
self.spawnWoodStation(-150);
|
||||||
self.spawnStation(100, .burn);
|
self.spawnBurnStation(100);
|
||||||
self.spawnStation(150, .burn);
|
self.spawnBurnStation(150);
|
||||||
|
|
||||||
|
self.eternal_flame_id = self.spawnEntity(Entity{
|
||||||
|
.type = .eternal_flame,
|
||||||
|
.pos = .init(200, 0)
|
||||||
|
}).?;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawnEntity(self: *App, e: Entity) ?EntityId {
|
||||||
|
return self.entities.insert(e) catch {
|
||||||
|
log.warn("Failed to spawn entity {}, entity limit reached", .{e.type});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn spawnMinion(self: *App, pos: Vec2) void {
|
pub fn spawnMinion(self: *App, pos: Vec2) void {
|
||||||
_ = self.entities.insert(Entity{
|
_ = self.spawnEntity(Entity{
|
||||||
.type = .minion,
|
.type = .minion,
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.has_animation = true,
|
.has_animation = true,
|
||||||
@ -292,44 +311,43 @@ pub fn spawnMinion(self: *App, pos: Vec2) void {
|
|||||||
.max_walk_speed = 300,
|
.max_walk_speed = 300,
|
||||||
|
|
||||||
.animation = .idle,
|
.animation = .idle,
|
||||||
}) catch {
|
});
|
||||||
log.warn("Failed to spawn minion, entity limit reached", .{});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawnStation(self: *App, pos: f32, subtype: Entity.StationSubtype) void {
|
pub fn spawnWoodStation(self: *App, pos: f32) void {
|
||||||
_ = self.entities.insert(Entity{
|
_ = self.spawnEntity(Entity{
|
||||||
.type = .station,
|
.type = .wood_station,
|
||||||
.station_subtype = subtype,
|
|
||||||
|
|
||||||
.pos = .init(pos, 0),
|
.pos = .init(pos, 0),
|
||||||
}) catch {
|
});
|
||||||
log.warn("Failed to spawn station, entity limit reached", .{});
|
}
|
||||||
return;
|
|
||||||
};
|
pub fn spawnBurnStation(self: *App, pos: f32) void {
|
||||||
|
_ = self.spawnEntity(Entity{
|
||||||
|
.type = .burn_station,
|
||||||
|
|
||||||
|
.pos = .init(pos, 0),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawnTree(self: *App, station_id: EntityId) void {
|
fn spawnTree(self: *App, station_id: EntityId) void {
|
||||||
const station = self.entities.get(station_id) orelse return;
|
const station = self.entities.get(station_id) orelse return;
|
||||||
assert(station.type == .station);
|
assert(station.type == .wood_station);
|
||||||
if (station.station_owned_tree != null) {
|
if (station.station_owned_tree != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const health = 10;
|
const health = 10;
|
||||||
const id = self.entities.insert(Entity{
|
if (self.spawnEntity(Entity{
|
||||||
.type = .tree,
|
.type = .tree,
|
||||||
.tree_health = health,
|
.tree_health = health,
|
||||||
.tree_max_health = health,
|
.tree_max_health = health,
|
||||||
|
|
||||||
.pos = station.pos
|
.pos = station.pos
|
||||||
}) catch {
|
})) |id| {
|
||||||
log.warn("Failed to spawn tree, entity limit reached", .{});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
station.station_owned_tree = id;
|
station.station_owned_tree = id;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn destroyTree(self: *App, tree_id: EntityId) void {
|
fn destroyTree(self: *App, tree_id: EntityId) void {
|
||||||
const tree = self.entities.get(tree_id) orelse return;
|
const tree = self.entities.get(tree_id) orelse return;
|
||||||
@ -341,12 +359,85 @@ fn destroyTree(self: *App, tree_id: EntityId) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn spawnWood(self: *App, pos: Vec2) void {
|
fn spawnWood(self: *App, pos: Vec2) void {
|
||||||
_ = self.entities.insert(Entity{
|
const health = 20;
|
||||||
|
|
||||||
|
_ = self.spawnEntity(Entity{
|
||||||
.type = .wood,
|
.type = .wood,
|
||||||
.pos = pos
|
.pos = pos,
|
||||||
}) catch {
|
.wood_health = health,
|
||||||
log.warn("Failed to spawn wood, entity limit reached", .{});
|
.wood_max_health = health
|
||||||
return;
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const EntityIteratorByDistance = struct {
|
||||||
|
arena: Allocator,
|
||||||
|
entities: *EntitySlotMap,
|
||||||
|
used: std.ArrayList(EntityId),
|
||||||
|
|
||||||
|
type: Entity.Type,
|
||||||
|
origin: Vec2,
|
||||||
|
|
||||||
|
pub fn next(self: *EntityIteratorByDistance) !?EntityId {
|
||||||
|
var nearest: ?EntityId = null;
|
||||||
|
var nearest_distance: f32 = 0;
|
||||||
|
|
||||||
|
var entity_iter = self.entities.iterator();
|
||||||
|
while (entity_iter.next()) |entity_id| {
|
||||||
|
const entity = self.entities.getAssumeExists(entity_id);
|
||||||
|
if (entity.type != self.type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const distance = entity.pos.distanceSqr(self.origin);
|
||||||
|
if (nearest == null or nearest_distance > distance) {
|
||||||
|
if (std.mem.indexOfScalar(EntityId, self.used.items, entity_id) != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nearest = entity_id;
|
||||||
|
nearest_distance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearest != null) {
|
||||||
|
try self.used.append(self.arena, nearest.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nearest;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const EntityIteratorByType = struct {
|
||||||
|
entities: *EntitySlotMap,
|
||||||
|
iter: EntitySlotMap.Iterator,
|
||||||
|
type: Entity.Type,
|
||||||
|
|
||||||
|
pub fn next(self: *EntityIteratorByType) ?EntityId {
|
||||||
|
while (self.iter.next()) |id| {
|
||||||
|
const entity = self.entities.getAssumeExists(id);
|
||||||
|
if (entity.type == self.type) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn initNearestEntityIter(self: *App, entity_type: Entity.Type, origin: Vec2, arena: Allocator) EntityIteratorByDistance {
|
||||||
|
return EntityIteratorByDistance{
|
||||||
|
.arena = arena,
|
||||||
|
.entities = &self.entities,
|
||||||
|
.used = .empty,
|
||||||
|
.origin = origin,
|
||||||
|
.type = entity_type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initEntityIteratorByType(self: *App, entity_type: Entity.Type) EntityIteratorByType {
|
||||||
|
return EntityIteratorByType{
|
||||||
|
.entities = &self.entities,
|
||||||
|
.iter = self.entities.iterator(),
|
||||||
|
.type = entity_type
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,137 +526,123 @@ fn minionLogic(self: *App, plt: Platform.Frame, minion_id: EntityId) !void {
|
|||||||
const minion = self.entities.getAssumeExists(minion_id);
|
const minion = self.entities.getAssumeExists(minion_id);
|
||||||
assert(minion.type == .minion);
|
assert(minion.type == .minion);
|
||||||
|
|
||||||
const goal_distance_threshold = 10;
|
const goal_distance = 10;
|
||||||
var walk_goal: ?Vec2 = null;
|
var walk_goal: ?Vec2 = null;
|
||||||
var station_goal: ?Entity.StationSubtype = null;
|
|
||||||
|
|
||||||
if (minion.minion_state == .goto_tree_station) {
|
self.ensureEntityExists(&minion.minion_carried_wood);
|
||||||
// if (minion.minion_carried_wood == null) {
|
minion.minion_chop_cooldown = @max(minion.minion_chop_cooldown - plt.dt, 0);
|
||||||
// if (self.getNearestFreeEntity(minion.pos, .tree)) |tree_id| {
|
|
||||||
// _ = tree_id; // autofix
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (minion.minion_state) {
|
switch (minion.minion_state) {
|
||||||
|
.wander => {
|
||||||
|
},
|
||||||
.goto_tree_station => {
|
.goto_tree_station => {
|
||||||
// TODO: Steal tree if it is nearer to you compared to the current minion that has it reserved.
|
if (minion.target_entity) |tree_station_id| {
|
||||||
if (self.getNearestFreeEntity(minion.pos, .tree)) |tree_id| {
|
const tree_station = self.entities.getAssumeExists(tree_station_id);
|
||||||
self.setEntityTarget(minion_id, tree_id);
|
walk_goal = tree_station.pos;
|
||||||
minion.minion_state = .chop_tree;
|
|
||||||
} else {
|
} else {
|
||||||
station_goal = .chop;
|
minion.minion_state = .wander;
|
||||||
|
|
||||||
if (minion.target_entity) |station_id| {
|
|
||||||
const station = self.entities.getAssumeExists(station_id);
|
|
||||||
if (station.type != .station) {
|
|
||||||
self.unsetEntityTarget(minion_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.chop_tree => {
|
.chop_tree => {
|
||||||
if (minion.target_entity) |tree_id| {
|
|
||||||
if (self.entities.getAssumeExists(tree_id).type != .tree) {
|
|
||||||
self.unsetEntityTarget(minion_id);
|
|
||||||
minion.minion_state = .goto_tree_station;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var is_at_tree = false;
|
|
||||||
if (minion.target_entity) |tree_id| {
|
if (minion.target_entity) |tree_id| {
|
||||||
const tree = self.entities.getAssumeExists(tree_id);
|
const tree = self.entities.getAssumeExists(tree_id);
|
||||||
|
|
||||||
is_at_tree = minion.pos.distance(tree.pos) < goal_distance_threshold;
|
if (minion.pos.distance(tree.pos) < goal_distance) {
|
||||||
if (is_at_tree) {
|
|
||||||
minion.minion_chop_cooldown = @max(minion.minion_chop_cooldown - plt.dt, 0);
|
|
||||||
if (minion.minion_chop_cooldown == 0) {
|
if (minion.minion_chop_cooldown == 0) {
|
||||||
minion.minion_chop_cooldown = std.time.ns_per_ms * 500;
|
minion.minion_chop_cooldown = std.time.ns_per_ms * 500;
|
||||||
tree.tree_health -= minion.minion_chop_damage;
|
tree.tree_health -= minion.minion_chop_damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tree.tree_health <= 0) {
|
if (tree.tree_health <= 0) {
|
||||||
minion.minion_state = .burn;
|
|
||||||
|
|
||||||
self.unsetEntityTarget(minion_id);
|
self.unsetEntityTarget(minion_id);
|
||||||
self.destroyTree(tree_id);
|
self.destroyTree(tree_id);
|
||||||
|
minion.minion_state = .wander;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
walk_goal = tree.pos;
|
walk_goal = tree.pos;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
minion.minion_state = .wander;
|
||||||
if (!is_at_tree) {
|
|
||||||
minion.minion_work_progress = 0;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.pickup_wood => {
|
.pickup_wood => {
|
||||||
},
|
if (minion.target_entity) |wood_id| {
|
||||||
.burn => {
|
const tree = self.entities.getAssumeExists(wood_id);
|
||||||
station_goal = .burn;
|
|
||||||
|
|
||||||
var is_at_station = false;
|
if (minion.pos.distance(tree.pos) < goal_distance) {
|
||||||
if (minion.target_entity) |station_id| {
|
|
||||||
const station = self.entities.getAssumeExists(station_id);
|
|
||||||
if (station.pos.distance(minion.pos) < goal_distance_threshold) {
|
|
||||||
is_at_station = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_at_station) {
|
|
||||||
minion.minion_work_progress = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_at_station) {
|
|
||||||
minion.minion_work_progress += minion.minion_burn_speed * plt.deltaTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
minion.minion_work_progress = std.math.clamp(minion.minion_work_progress, 0, 1);
|
|
||||||
if (minion.minion_work_progress == 1) {
|
|
||||||
minion.minion_state = .goto_tree_station;
|
|
||||||
minion.minion_work_progress = 0;
|
|
||||||
self.heat += 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (station_goal) |station_type| {
|
|
||||||
if (minion.target_entity) |entity_id| {
|
|
||||||
if (self.entities.get(entity_id)) |station| {
|
|
||||||
if (station.type != .station) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minion.target_entity) |station_id| {
|
|
||||||
const station = self.entities.getAssumeExists(station_id);
|
|
||||||
if (station.station_subtype != station_type) {
|
|
||||||
self.unsetEntityTarget(minion_id);
|
self.unsetEntityTarget(minion_id);
|
||||||
|
self.minionPickupWood(minion_id, wood_id);
|
||||||
|
minion.minion_state = .wander;
|
||||||
|
} else {
|
||||||
|
walk_goal = tree.pos;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
minion.minion_state = .wander;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.goto_burn_station => {
|
||||||
|
if (minion.minion_carried_wood == null) {
|
||||||
|
self.unsetEntityTarget(minion_id);
|
||||||
|
minion.minion_state = .wander;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minion.target_entity == null) {
|
if (minion.target_entity) |burn_station_id| {
|
||||||
if (self.getNearestFreeStation(minion.pos, station_type)) |station_id| {
|
const burn_station = self.entities.getAssumeExists(burn_station_id);
|
||||||
self.setEntityTarget(minion_id, station_id);
|
if (minion.pos.distance(burn_station.pos) < goal_distance) {
|
||||||
|
minion.minion_state = .burn_wood;
|
||||||
|
} else {
|
||||||
|
walk_goal = burn_station.pos;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
minion.minion_state = .wander;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
.burn_wood => {
|
||||||
|
if (minion.minion_carried_wood) |wood_id| {
|
||||||
|
const wood = self.entities.getAssumeExists(wood_id);
|
||||||
|
const eternal_flame = self.entities.getAssumeExists(self.eternal_flame_id);
|
||||||
|
|
||||||
if (minion.target_entity) |station_id| {
|
wood.pos = eternal_flame.pos;
|
||||||
const station = self.entities.getAssumeExists(station_id);
|
wood.wood_burning = true;
|
||||||
walk_goal = station.pos;
|
wood.wood_carried_by = null;
|
||||||
|
minion.minion_carried_wood = null;
|
||||||
|
minion.minion_throw_cooldown = std.time.ns_per_s;
|
||||||
|
} else {
|
||||||
|
minion.minion_throw_cooldown = @max(minion.minion_throw_cooldown - plt.dt, 0);
|
||||||
|
if (minion.minion_throw_cooldown == 0) {
|
||||||
|
minion.minion_state = .wander;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
if (walk_goal) |pos| {
|
if (walk_goal) |pos| {
|
||||||
const diff = minion.pos.x - pos.x;
|
const diff = minion.pos.x - pos.x;
|
||||||
if (diff < -goal_distance_threshold) {
|
if (diff < -goal_distance) {
|
||||||
minion.acc = .init(minion.max_walk_speed, 0);
|
minion.acc = .init(minion.max_walk_speed, 0);
|
||||||
} else if (diff > goal_distance_threshold) {
|
} else if (diff > goal_distance) {
|
||||||
minion.acc = .init(-minion.max_walk_speed, 0);
|
minion.acc = .init(-minion.max_walk_speed, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn minionPickupWood(self: *App, minion_id: EntityId, wood_id: EntityId) void {
|
||||||
|
const minion = self.entities.get(minion_id) orelse return;
|
||||||
|
assert(minion.type == .minion);
|
||||||
|
|
||||||
|
const wood = self.entities.get(wood_id) orelse return;
|
||||||
|
assert(wood.type == .wood);
|
||||||
|
|
||||||
|
wood.wood_carried_by = minion_id;
|
||||||
|
minion.minion_carried_wood = wood_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensureEntityExists(self: *App, maybe_entity_id: *?EntityId) void {
|
||||||
|
if (maybe_entity_id.* != null and !self.entities.exists(maybe_entity_id.*.?)) {
|
||||||
|
maybe_entity_id.* = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn frame(self: *App, plt: Platform.Frame) !void {
|
pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||||
const input = plt.input;
|
const input = plt.input;
|
||||||
_ = input; // autofix
|
_ = input; // autofix
|
||||||
@ -589,57 +666,166 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
|||||||
Gfx.setClearColor(.rgb(40, 40, 40));
|
Gfx.setClearColor(.rgb(40, 40, 40));
|
||||||
Gfx.drawLine(.init(-800, 0), .init(800, 0), .white, 5);
|
Gfx.drawLine(.init(-800, 0), .init(800, 0), .white, 5);
|
||||||
|
|
||||||
var station_iter = self.entities.iterator();
|
{ // Spawn trees at wood station on a cooldown
|
||||||
while (station_iter.next()) |station_id| {
|
var wood_station_iter = self.initEntityIteratorByType(.wood_station);
|
||||||
const station = self.entities.getAssumeExists(station_id);
|
while (wood_station_iter.next()) |wood_station_id| {
|
||||||
if (station.type != .station) {
|
const wood_station = self.entities.getAssumeExists(wood_station_id);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (station.station_owned_tree) |station_owned_tree| {
|
if (wood_station.station_owned_tree) |station_owned_tree| {
|
||||||
if (!self.entities.exists(station_owned_tree)) {
|
if (!self.entities.exists(station_owned_tree)) {
|
||||||
station.station_owned_tree = null;
|
wood_station.station_owned_tree = null;
|
||||||
station.station_spawn_tree_cooldown = std.time.ns_per_s;
|
wood_station.station_spawn_tree_cooldown = std.time.ns_per_s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (station.station_subtype == .chop) {
|
wood_station.station_spawn_tree_cooldown = @max(wood_station.station_spawn_tree_cooldown - plt.dt, 0);
|
||||||
station.station_spawn_tree_cooldown = @max(station.station_spawn_tree_cooldown - plt.dt, 0);
|
if (wood_station.station_spawn_tree_cooldown == 0 and wood_station.station_owned_tree == null) {
|
||||||
if (station.station_spawn_tree_cooldown == 0 and station.station_owned_tree == null) {
|
self.spawnTree(wood_station_id);
|
||||||
self.spawnTree(station_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const character_size = Vec2.init(96, 96);
|
{ // Ensure that `target_entity` and `targeted_by_entity` don't point to dead entities
|
||||||
|
|
||||||
{
|
|
||||||
var entity_iter = self.entities.iterator();
|
var entity_iter = self.entities.iterator();
|
||||||
while (entity_iter.next()) |entity_id| {
|
while (entity_iter.next()) |entity_id| {
|
||||||
const e = self.entities.getAssumeExists(entity_id);
|
const e = self.entities.getAssumeExists(entity_id);
|
||||||
|
|
||||||
|
self.ensureEntityExists(&e.target_entity);
|
||||||
|
self.ensureEntityExists(&e.targeted_by_entity);
|
||||||
|
|
||||||
if (e.target_entity) |other_id| {
|
if (e.target_entity) |other_id| {
|
||||||
if (self.entities.get(other_id)) |other| {
|
const other = self.entities.getAssumeExists(other_id);
|
||||||
if (other.targeted_by_entity != entity_id) {
|
if (other.targeted_by_entity != entity_id) {
|
||||||
e.target_entity = null;
|
e.target_entity = null;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
e.target_entity = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.targeted_by_entity) |other_id| {
|
if (e.targeted_by_entity) |other_id| {
|
||||||
if (self.entities.get(other_id)) |other| {
|
const other = self.entities.getAssumeExists(other_id);
|
||||||
if (other.target_entity != entity_id) {
|
if (other.target_entity != entity_id) {
|
||||||
e.targeted_by_entity = null;
|
e.targeted_by_entity = null;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
e.targeted_by_entity = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const burn_speed = 10;
|
||||||
|
|
||||||
|
var wood_iter = self.initEntityIteratorByType(.wood);
|
||||||
|
while (wood_iter.next()) |wood_id| {
|
||||||
|
const wood = self.entities.getAssumeExists(wood_id);
|
||||||
|
|
||||||
|
self.ensureEntityExists(&wood.wood_carried_by);
|
||||||
|
|
||||||
|
// Apply burn
|
||||||
|
if (wood.wood_burning) {
|
||||||
|
wood.wood_health = @max(wood.wood_health - burn_speed * dt, 0);
|
||||||
|
|
||||||
|
if (wood.wood_health == 0) {
|
||||||
|
self.heat += 1;
|
||||||
|
self.entities.removeAssumeExists(wood_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each wood entity will assign itself to the nearest minion that isn't busy
|
||||||
|
if (wood.wood_carried_by == null and (wood.targeted_by_entity == null or wood.wood_carried_by == null)) {
|
||||||
|
var minion_iter = self.initNearestEntityIter(.minion, wood.pos, plt.arena);
|
||||||
|
while (try minion_iter.next()) |minion_id| {
|
||||||
|
const minion = self.entities.getAssumeExists(minion_id);
|
||||||
|
if (minion.minion_carried_wood != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = minion.minion_state;
|
||||||
|
if (state == .goto_tree_station or state == .wander) {
|
||||||
|
self.setEntityTarget(minion_id, wood_id);
|
||||||
|
minion.minion_state = .pickup_wood;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Each tree entity will assign itself to the nearest minion that isn't busy
|
||||||
|
var tree_iter = self.initEntityIteratorByType(.tree);
|
||||||
|
while (tree_iter.next()) |tree_id| {
|
||||||
|
const tree = self.entities.getAssumeExists(tree_id);
|
||||||
|
if (tree.targeted_by_entity != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var minion_iter = self.initNearestEntityIter(.minion, tree.pos, plt.arena);
|
||||||
|
while (try minion_iter.next()) |minion_id| {
|
||||||
|
const minion = self.entities.getAssumeExists(minion_id);
|
||||||
|
if (minion.minion_carried_wood != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = minion.minion_state;
|
||||||
|
if (state == .wander or state == .goto_tree_station) {
|
||||||
|
self.setEntityTarget(minion_id, tree_id);
|
||||||
|
minion.minion_state = .chop_tree;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Each wood station will assign itself to the nearest minion that isn't busy
|
||||||
|
var wood_station_iter = self.initEntityIteratorByType(.wood_station);
|
||||||
|
while (wood_station_iter.next()) |wood_station_id| {
|
||||||
|
const wood_station = self.entities.getAssumeExists(wood_station_id);
|
||||||
|
if (wood_station.targeted_by_entity != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var minion_iter = self.initNearestEntityIter(.minion, wood_station.pos, plt.arena);
|
||||||
|
while (try minion_iter.next()) |minion_id| {
|
||||||
|
const minion = self.entities.getAssumeExists(minion_id);
|
||||||
|
if (minion.minion_carried_wood != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = minion.minion_state;
|
||||||
|
if (state == .wander) {
|
||||||
|
self.setEntityTarget(minion_id, wood_station_id);
|
||||||
|
minion.minion_state = .goto_tree_station;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Each burn station will assign itself to the nearest minion that has wood
|
||||||
|
var burn_station_iter = self.initEntityIteratorByType(.burn_station);
|
||||||
|
while (burn_station_iter.next()) |burn_station_id| {
|
||||||
|
const burn_station = self.entities.getAssumeExists(burn_station_id);
|
||||||
|
if (burn_station.targeted_by_entity != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var minion_iter = self.initNearestEntityIter(.minion, burn_station.pos, plt.arena);
|
||||||
|
while (try minion_iter.next()) |minion_id| {
|
||||||
|
const minion = self.entities.getAssumeExists(minion_id);
|
||||||
|
if (minion.minion_carried_wood == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = minion.minion_state;
|
||||||
|
if (state == .wander) {
|
||||||
|
self.setEntityTarget(minion_id, burn_station_id);
|
||||||
|
minion.minion_state = .goto_burn_station;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
var minion_iter = self.entities.iterator();
|
var minion_iter = self.entities.iterator();
|
||||||
while (minion_iter.next()) |minion_id| {
|
while (minion_iter.next()) |minion_id| {
|
||||||
const minion = self.entities.getAssumeExists(minion_id);
|
const minion = self.entities.getAssumeExists(minion_id);
|
||||||
@ -660,13 +846,25 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
|||||||
minion.animation_dir = .down;
|
minion.animation_dir = .down;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var tree_iter = self.entities.iterator();
|
var wood_iter = self.initEntityIteratorByType(.wood);
|
||||||
|
while (wood_iter.next()) |wood_id| {
|
||||||
|
const wood = self.entities.getAssumeExists(wood_id);
|
||||||
|
|
||||||
|
if (wood.wood_carried_by) |minion_id| {
|
||||||
|
const minion = self.entities.getAssumeExists(minion_id);
|
||||||
|
wood.pos = minion.pos.add(.init(0, -50));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tree_iter = self.initEntityIteratorByType(.tree);
|
||||||
while (tree_iter.next()) |id| {
|
while (tree_iter.next()) |id| {
|
||||||
const e = self.entities.getAssumeExists(id);
|
const e = self.entities.getAssumeExists(id);
|
||||||
|
|
||||||
if (e.type == .tree) {
|
|
||||||
const tree_size = Vec2.init(40, 100);
|
const tree_size = Vec2.init(40, 100);
|
||||||
Gfx.drawRectangle(e.pos.sub(.init(tree_size.x/2, tree_size.y)), tree_size, .rgb(40, 200, 40));
|
Gfx.drawRectangle(e.pos.sub(.init(tree_size.x/2, tree_size.y)), tree_size, .rgb(40, 200, 40));
|
||||||
|
|
||||||
@ -674,8 +872,8 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
|||||||
drawProgressBar(progress, e.pos.add(.init(0, 20)), .init(40, 10), .rgb(0, 100, 20));
|
drawProgressBar(progress, e.pos.add(.init(0, 20)), .init(40, 10), .rgb(0, 100, 20));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
const character_size = Vec2.init(96, 96);
|
||||||
{
|
{
|
||||||
var entity_iter = self.entities.iterator();
|
var entity_iter = self.entities.iterator();
|
||||||
while (entity_iter.next()) |id| {
|
while (entity_iter.next()) |id| {
|
||||||
@ -735,18 +933,15 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
|||||||
);
|
);
|
||||||
Gfx.drawSprite(sprite, pos, size, .white);
|
Gfx.drawSprite(sprite, pos, size, .white);
|
||||||
|
|
||||||
const is_working = e.minion_state == .chop_tree or e.minion_state == .burn;
|
if (e.type == .minion and e.minion_state == .burn_wood and e.minion_throw_cooldown > 0) {
|
||||||
if (e.type == .minion and is_working and e.minion_work_progress > 0) {
|
Gfx.drawRectangle(e.pos.sub(.init(5, 65)), .init(10, 10), .rgb(200, 20, 20));
|
||||||
drawProgressBar(e.minion_work_progress, e.pos.add(.init(0, 20)), .init(100, 10), .rgb(0, 200, 20));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.type == .station) {
|
if (e.type == .wood_station) {
|
||||||
if (e.station_subtype == .chop) {
|
|
||||||
Gfx.drawRectangle(e.pos.sub(.init(5, 5)), .init(10, 10), .rgb(20, 200, 20));
|
Gfx.drawRectangle(e.pos.sub(.init(5, 5)), .init(10, 10), .rgb(20, 200, 20));
|
||||||
} else {
|
} else if (e.type == .burn_station) {
|
||||||
Gfx.drawRectangle(e.pos.sub(.init(5, 5)), .init(10, 10), .rgb(200, 20, 20));
|
Gfx.drawRectangle(e.pos.sub(.init(5, 5)), .init(10, 10), .rgb(200, 20, 20));
|
||||||
}
|
|
||||||
} else if (e.type == .minion) {
|
} else if (e.type == .minion) {
|
||||||
Gfx.drawText(self.font, .{
|
Gfx.drawText(self.font, .{
|
||||||
.pos = e.pos.sub(.init(20, 50)),
|
.pos = e.pos.sub(.init(20, 50)),
|
||||||
@ -757,13 +952,19 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{ // Draw eternal flame
|
||||||
var wood_iter = self.entities.iterator();
|
var eternal_flame_iter = self.initEntityIteratorByType(.eternal_flame);
|
||||||
|
while (eternal_flame_iter.next()) |id| {
|
||||||
|
const eternal_flame = self.entities.getAssumeExists(id);
|
||||||
|
|
||||||
|
Gfx.drawRectangle(eternal_flame.pos.sub(.init(10, 50)), .init(30, 50), .rgb(150, 30, 20));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Draw wood
|
||||||
|
var wood_iter = self.initEntityIteratorByType(.wood);
|
||||||
while (wood_iter.next()) |id| {
|
while (wood_iter.next()) |id| {
|
||||||
const wood = self.entities.getAssumeExists(id);
|
const wood = self.entities.getAssumeExists(id);
|
||||||
if (wood.type != .wood) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Gfx.drawRectangle(wood.pos.sub(.init(10, 10)), .init(20, 20), .rgb(100, 100, 20));
|
Gfx.drawRectangle(wood.pos.sub(.init(10, 10)), .init(20, 20), .rgb(100, 100, 20));
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user