use package core
use package glfw3
+Entity_ID :: u32
+Entity_Nothing :: cast(Entity_ID) 0
+
Entity :: struct {
- id: u32;
+ id: Entity_ID;
type: type_expr;
flags: Entity_Flags;
Entity_Flags :: enum #flags {
Interactable;
Solid;
+ Carryable;
}
IsEntity :: interface (e: $E) {
Entity_Manager :: struct {
entity_allocator: Allocator;
+
+ // Hmmm... Do they need to be stored in both ways?
entities: [..] ^Entity;
+ entity_map: Map(Entity_ID, ^Entity);
+
entity_types: Map(type_expr, Entity_Handles);
- next_entity_id: u32;
+ next_entity_id: Entity_ID;
register :: entity_manager_register;
add :: entity_manager_add;
make :: entity_manager_make;
update :: entity_manager_update;
draw :: entity_manager_draw;
+ get :: entity_manager_get;
query :: entity_manager_query;
query_by_type :: entity_manager_query_by_type;
query_by_flags :: entity_manager_query_by_flags;
next_entity_id += 1;
entities << entity;
+ entity_map[entity.id] = entity;
return entity.id;
}
}
}
+entity_manager_get :: (use this: ^Entity_Manager, id: Entity_ID) => entity_map[id];
+
entity_manager_query :: (use this: ^Entity_Manager, area: Rect) -> [] ^Entity {
ents: [..] ^Entity;
for entities {
expected_flags := cast(u32) flags;
for entities {
- if (~~ it.flags & expected_flags) != expected_flags do continue;
+ if ~~ it.flags & expected_flags != expected_flags do continue;
get_rect := entity_types[it.type].get_rect;
if get_rect == null_proc do continue;
return ents;
}
-entity_is :: macro (entity: ^Entity, type: type_expr, body: Code) {
+if_entity_is :: macro (entity: ^Entity, type: type_expr, body: Code) {
if entity.type == type {
it := cast(^type) entity;
#insert body;
}
}
+
+switch_entity :: macro (entity: ^Entity, body: Code) {
+ switch entity.type do #insert body;
+}
+
+entity_case :: macro (type: type_expr, body: Code) {
+ case type {
+ object := cast(^type) it;
+ #insert body;
+ }
+}
use package core
use package glfw3
+Player_Controls :: struct {
+ up := GLFW_KEY_W;
+ down := GLFW_KEY_S;
+ left := GLFW_KEY_A;
+ right := GLFW_KEY_D;
+ interact := GLFW_KEY_F;
+ pick_up := GLFW_KEY_G;
+}
+
Player :: struct {
use entity: Entity;
- health := 69.0f;
+ holding: Entity_ID;
+
+ controls: Player_Controls;
Size :: 16.0f
init_data :: struct {
pos := Vector2.{400, 200};
+ controls := Player_Controls.{};
}
init :: (use this: ^Player, data: init_data) {
this.pos = data.pos;
+ this.holding = Entity_Nothing;
+
+ this.controls = data.controls;
}
get_rect :: (use this: ^Player) => Rect.{ pos.x - Size, pos.y - Size, Size * 2, Size * 2 };
speed :: Size * 8;
delta: Vector2;
- if is_key_down(GLFW_KEY_W) do delta.y -= speed * dt;
- if is_key_down(GLFW_KEY_S) do delta.y += speed * dt;
- if is_key_down(GLFW_KEY_A) do delta.x -= speed * dt;
- if is_key_down(GLFW_KEY_D) do delta.x += speed * dt;
+ if is_key_down(controls.up) do delta.y -= speed * dt;
+ if is_key_down(controls.down) do delta.y += speed * dt;
+ if is_key_down(controls.left) do delta.x -= speed * dt;
+ if is_key_down(controls.right) do delta.x += speed * dt;
walls := entity_manager->query_by_flags(.{pos.x - 100, pos.y - 100, 200, 200}, .Solid);
defer memory.free_slice(^walls);
try_move(this, .{delta.x, 0}, walls);
try_move(this, .{0, delta.y}, walls);
- if is_key_just_up(GLFW_KEY_SPACE) {
+ //
+ // Try to pick up nearby objects
+ //
+ if is_key_just_up(controls.pick_up) {
+ if holding != Entity_Nothing {
+ holding_object := entity_manager->get(holding);
+ holding_object.pos = pos + .{20, 0};
+ holding = Entity_Nothing;
+
+ } else {
+ area := Rect.{pos.x - Size * 2, pos.y - Size * 2, Size * 4, Size * 4};
+ objects := entity_manager->query_by_flags(area, .Carryable);
+ defer memory.free_slice(^objects);
+
+ // This should first sort by the distance to the object
+ target_object := objects[0];
+ this.holding = target_object.id;
+
+ // Should this fire an event on the target object?
+ }
+ }
+
+ //
+ // Try to interact with nearby objects
+ //
+ if is_key_just_up(controls.interact) {
area := Rect.{pos.x - Size * 2, pos.y - Size * 2, Size * 4, Size * 4};
- objects := entity_manager->query_by_flags(area, Entity_Flags.Solid | .Interactable);
+ objects := entity_manager->query_by_flags(area, .Interactable);
defer memory.free_slice(^objects);
for objects {
vtable.interact(it, this);
}
}
+
+ //
+ // Adjust the position of the entity you are carrying
+ // to be above the players head.
+ //
+ if holding != Entity_Nothing {
+ holding_object := entity_manager->get(holding);
+ vtable := ^entity_manager.entity_types[holding_object.type];
+ if vtable.get_rect != null_proc {
+ r := vtable.get_rect(holding_object);
+ holding_object.pos = pos - .{0, Size + r.h};
+ }
+ }
}
try_move :: (use this: ^Player, delta: Vector2, obsticles: [] ^Entity) {
ent_rect := get_rect(this);
for obsticles {
+ // Don't check collision on the object that you are carrying because it probably won't make sense.
+ if it.id == this.holding do continue;
+
vtable := ^entity_manager.entity_types[it.type];
if Rect.intersects(ent_rect, vtable.get_rect(~~ it)) {
pos -= delta;
this.flags |= .Interactable;
this.flags |= .Solid;
+ this.flags |= .Carryable;
}
get_rect :: (use this: ^Door) => Rect.{ pos.x, pos.y, size.x * (1 - openness), size.y };
printf("The door at {} was interacted with by a {}!\n", pos, interactor.type);
// Doors only open if interacted with by a player
- entity_is(interactor, Player) {
- if target_openness > 0 do target_openness = 0;
- else do target_openness = 0.8f;
-
- dist := Vector2.mag(this.pos - interactor.pos);
- printf("The player is {} units away and has {.2} health.\n", dist, it.health);
+ if_entity_is(interactor, Player) {
+ target_openness = 0.8f - target_openness;
}
}
}
}
+
+Item :: struct {
+ use entity: Entity;
+
+ Size :: 8.0f;
+
+ color: Color;
+
+ init_data :: struct {
+ pos := Vector2.{0, 0};
+ color := Color.{1, 0, 1, 1};
+ }
+
+ init :: (use this: ^Item, data: init_data) {
+ this.pos = data.pos;
+ this.color = data.color;
+ this.flags |= .Carryable;
+ }
+
+ get_rect :: (use this: ^Item) => Rect.{ pos.x - Size, pos.y - Size, Size * 2, Size * 2 };
+
+ draw :: (use this: ^Item) {
+ immediate_set_color(this.color);
+
+ r := this->get_rect();
+ immediate_rectangle(r.x, r.y, r.w, r.h);
+ }
+}
+
+
+
@Relocate
move_towards :: (v: ^$T, target: T, diff: T) {
if math.abs(target - *v) <= diff {