id = 10
flags = 2
pos.x = 176.0000
-pos.y = 312.0000
+pos.y = 88.0000
size.x = 352.0000
size.y = 16.0000
:RenderComponent
+layer = 0
color.r = 1.0000
color.g = 1.0000
color.b = 1.0000
id = 11
flags = 3
pos.x = 440.0000
-pos.y = 160.0000
+pos.y = 48.0000
size.x = 16.0000
-size.y = 320.0000
+size.y = 96.0000
:RenderComponent
+layer = 0
color.r = 1.0000
color.g = 1.0000
color.b = 1.0000
[Player]
id = 12
flags = 2
-pos.x = 544.3174
-pos.y = 236.6171
+pos.x = 390.5043
+pos.y = 187.4996
size.x = 32.0000
size.y = 32.0000
:MovementComponent
controls.right = 68
controls.interact = 70
controls.pick_up = 71
-facing = 4
+facing = 2
:PlayerComponent
holding = 0
:RenderComponent
+layer = 0
color.r = 0.0000
color.g = 1.0000
color.b = 0.0000
id = 13
flags = 3
pos.x = 352.0000
-pos.y = 312.0000
+pos.y = 88.0000
size.x = 160.0000
size.y = 16.0000
:DoorComponent
target_openness = 0.8000
openness = 0.8000
:RenderComponent
+layer = 0
color.r = 1.0000
color.g = 1.0000
color.b = 1.0000
color.a = 1.0000
-[Item_Entity]
-id = 14
-flags = 4
-pos.x = 508.3851
-pos.y = 375.2260
-size.x = 16.0000
-size.y = 16.0000
-:RenderComponent
-color.r = 1.0000
-color.g = 1.0000
-color.b = 1.0000
-color.a = 1.0000
-:ItemComponent
-item = "beer"
-
-[Item_Entity]
-id = 15
-flags = 4
-pos.x = 555.2645
-pos.y = 298.4719
-size.x = 16.0000
-size.y = 16.0000
-:RenderComponent
-color.r = 1.0000
-color.g = 1.0000
-color.b = 1.0000
-color.a = 1.0000
-:ItemComponent
-item = "beer"
-
[Tap]
-id = 16
+id = 14
flags = 3
-pos.x = 576.0000
-pos.y = 160.0000
+pos.x = 32.0000
+pos.y = 16.0000
size.x = 32.0000
size.y = 32.0000
:RenderComponent
-color.r = 0.1000
-color.g = 0.1000
+layer = 1
+color.r = 0.2000
+color.g = 0.2000
color.b = 1.0000
color.a = 1.0000
:DispenserComponent
item = "beer"
-max_timeout = 5.0000
+max_timeout = 2.0000
--- /dev/null
+
+use package core
+use package glfw3
+
+
+Facing :: enum {
+ Up :: 0x01;
+ Down :: 0x02;
+ Left :: 0x03;
+ Right :: 0x04;
+}
+
+facing_to_direction_vector :: macro (f: Facing) -> Vector2 {
+ switch f {
+ case .Up do return .{0, -1};
+ case .Down do return .{0, 1};
+ case .Left do return .{-1, 0};
+ case .Right do return .{1, 0};
+ }
+}
+
+
+MovementComponent :: struct {
+ use component: Component;
+ controls : Player_Controls;
+ facing := Facing.Up;
+
+ update :: (movement: ^MovementComponent, use this: ^Entity, dt: f32) {
+ speed :: 128.0f;
+
+ delta: Vector2;
+ if is_key_down(movement.controls.left) { delta.x -= speed * dt; movement.facing = .Left; }
+ if is_key_down(movement.controls.right) { delta.x += speed * dt; movement.facing = .Right; }
+ if is_key_down(movement.controls.up) { delta.y -= speed * dt; movement.facing = .Up; }
+ if is_key_down(movement.controls.down) { delta.y += speed * dt; movement.facing = .Down; }
+
+ dist := math.max(size.x, size.y) * 2;
+ walls := scene->query_by_flags(.{pos.x - dist, pos.y - dist, dist * 2, dist * 2}, .Solid);
+ defer memory.free_slice(^walls);
+
+ try_move(this, .{delta.x, 0}, walls);
+ try_move(this, .{0, delta.y}, walls);
+ }
+
+ try_move :: (use this: ^Entity, delta: Vector2, obsticles: [] ^Entity) {
+ pos += delta;
+ ent_rect := Entity.get_rect(this);
+
+ holding: Entity_ID;
+ if player := this->get(PlayerComponent); player != null {
+ holding = player.holding;
+ }
+
+ for obsticles {
+ // Don't check collision on the object that you are carrying or yourself because it probably won't make sense.
+ if it == this do continue;
+ if it.id == holding do continue;
+
+ if Rect.intersects(ent_rect, Entity.get_rect(it)) {
+ pos -= delta;
+ break;
+ }
+ }
+ }
+}
+
+
--- /dev/null
+
+use package core
+use package glfw3
+
+PlayerComponent :: struct {
+ use base: Component;
+
+ holding : Entity_ID;
+
+ update :: (player: ^PlayerComponent, use this: ^Entity, dt: f32) {
+ // Highlight the object that you are going to interact with
+
+ movement := this->get(MovementComponent);
+
+ // Try to interact with nearby objects
+ //
+ if is_key_just_up(movement.controls.interact) {
+ area := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
+ objects := scene->query_by_component(area, InteractableComponent);
+ defer memory.free_slice(^objects);
+
+ for objects {
+ interact_comp := it->get(InteractableComponent);
+ interact_comp.interact(it, this);
+ }
+ }
+
+ //
+ // Try to pick up nearby objects
+ //
+ if is_key_just_up(movement.controls.pick_up) {
+ if player.holding != Entity_Nothing {
+ holding_object := scene->get(player.holding);
+ holding_object.flags |= .Carryable;
+ r := Entity.get_rect(holding_object);
+
+ d := Vector2.{(size.x + r.w) / 2 + 4, (size.y + r.h) / 2 + 4};
+ holding_object.pos = pos + facing_to_direction_vector(movement.facing) * d;
+ player.holding = Entity_Nothing;
+
+ } else {
+ area := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
+ objects := scene->query_by_flags(area, .Carryable);
+ defer memory.free_slice(^objects);
+
+ // This should first sort by the distance to the object
+ while i := 0; i < objects.count {
+ defer i += 1;
+ if objects[i] == this do continue;
+
+ target_object := objects[i];
+ target_object.flags ^= .Carryable; // This only works because we assume that it is Carryable, otherwise this would make it carryable.
+ player.holding = target_object.id;
+ break;
+ }
+
+ // Should this fire an event on the target object?
+ }
+ }
+
+ //
+ // Adjust the position of the entity you are carrying
+ // to be above the players head.
+ //
+ if player.holding != Entity_Nothing {
+ holding_object := scene->get(player.holding);
+ r := Entity.get_rect(holding_object);
+ holding_object.pos = pos - .{0, (size.y + r.h) / 2};
+ }
+ }
+
+ render :: (use this: ^Entity) {
+ rect := Entity.get_rect(this);
+ immediate_image(^Player.assets.texture, rect.x, rect.y, rect.w, rect.h);
+ }
+}
if active_index < 0 do return;
mouse_pos := mouse_get_position_vector() - scene_render_offset;
- if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) && mouse_pos.x < ~~window_width - sidebar_width && mouse_pos.y > menubar_height {
+ if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) && mouse_pos.x < ~~window_width - sidebar_width {
schematic := scene.schematics.entries[active_index].value;
entity := schematic.create(^scene);
entity.schematic = schematic.type;
#local handle_entity_selction_and_dragging :: (dt: f32) {
mouse_pos := mouse_get_position_vector() - scene_render_offset;
- if mouse_pos.x < ~~window_width - sidebar_width && mouse_pos.y > menubar_height {
+ if mouse_pos.x < ~~window_width - sidebar_width {
if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) || is_button_just_down(GLFW_MOUSE_BUTTON_RIGHT) {
if active_tab == .Edit do active_index = -1;
selected_entity_id = Entity_Nothing;
--- /dev/null
+
+use package core
+use package glfw3
+
+
+#local Wall :: struct {
+ render :: (use this: ^Entity) {
+ r := Entity.get_rect(this);
+ immediate_rectangle(r.x, r.y, r.w, r.h);
+ }
+
+ #struct_tag Entity_Schematic.{
+ (scene) => wall_create(scene, .{0,0}, .{0,0})
+ }
+}
+
+wall_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity {
+ this := scene->make();
+ this.pos = pos;
+ this.size = size;
+ this.flags |= .Solid;
+
+ scene->create_and_add(this, RenderComponent) {
+ comp.func = Wall.render;
+ }
+
+ return this;
+}
+
+#local Door :: struct {
+ create :: (scene) => door_create(scene, .Zero, .Zero);
+
+ render :: (use this: ^Entity) {
+ immediate_set_color(.{0.7, 0.7, 0.1});
+
+ r := Entity.get_rect(this);
+ immediate_rectangle(r.x, r.y, r.w, r.h);
+ }
+
+ get_rect :: (use this: ^Entity) => {
+ door := this->get(DoorComponent);
+ size_x := size.x * (1 - door.openness);
+ return Rect.{ pos.x - size_x / 2, pos.y - size.y / 2, size_x, size.y };
+ }
+
+ interact :: (use this: ^Entity, interactor: ^Entity) {
+ // Doors only open if interacted with by a player
+ if interactor->has(PlayerComponent) {
+ door := this->get(DoorComponent);
+ door->toggle_open();
+ }
+ }
+
+ #struct_tag Entity_Schematic.{create}
+}
+
+DoorComponent :: struct {
+ use base: Component;
+
+ max_openness := 0.8f;
+ target_openness := 0.0f;
+ openness := 0.0f;
+
+ update :: (use this: ^DoorComponent, door: ^Entity, dt: f32) {
+ if openness != target_openness {
+ move_towards(^openness, target_openness, dt * 2);
+ }
+ }
+
+ toggle_open :: (use this: ^DoorComponent) {
+ target_openness = max_openness - target_openness;
+ }
+}
+
+door_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity {
+ this := scene->make();
+ this.pos = pos;
+ this.size = size;
+
+ this.flags |= .Interactable;
+ this.flags |= .Solid;
+
+ scene->create_and_add(this, DoorComponent) {}
+ scene->create_and_add(this, RenderComponent) { comp.func = Door.render; }
+ scene->create_and_add(this, SizeComponent) { comp.func = Door.get_rect; }
+ scene->create_and_add(this, InteractableComponent) { comp.interact = Door.interact; }
+
+ return this;
+}
+
+
+
+@Relocate
+move_towards :: (v: ^$T, target: T, diff: T) {
+ if math.abs(target - *v) <= diff {
+ *v = target;
+ return;
+ }
+
+ if *v < target do *v += diff;
+ if *v > target do *v -= diff;
+}
this.size = .{0, 0};
this.flags |= .Carryable;
- scene->create_and_add(this, RenderComponent) { comp.func = render; }
+ scene->create_and_add(this, RenderComponent) { comp.func = render; comp.layer = 10; }
scene->create_and_add(this, ItemComponent) {}
return this;
}
}
entity_manager_draw :: (use this: ^Entity_Manager) {
- // Entities should be sorted by z-order.
-
+
+ //
+ // This is a rather expensive check.
+ // It could probably be done solely when entities are added/removed.
+ array.sort(entities, (e1, e2) => {
+ e1_comp := e1->get(RenderComponent);
+ e2_comp := e2->get(RenderComponent);
+
+ e1_layer := e1_comp.layer if e1_comp != null else 0;
+ e2_layer := e2_comp.layer if e2_comp != null else 0;
+
+ return e1_layer - e2_layer;
+ });
for entities {
render_comp := it->get(RenderComponent);
entity_manager_get :: (use this: ^Entity_Manager, id: Entity_ID) => entity_map[id];
-entity_manager_delete :: (use this: ^Entity_Manager, ent: ^Entity) {
+entity_manager_delete :: (use this: ^Entity_Manager, ent: ^Entity, delete_from_array := true) {
map.delete(^entity_map, ent.id);
- array.remove(^entities, ent); // This preserves the order of the entities, but does that matter?
+
+ //
+ // Not deleting the entity from the array, because this messes with the
+ // order, and becaues of that, it breaks a for-loop.
+ if delete_from_array do array.remove(^entities, ent); // This preserves the order of the entities, but does that matter?
// This assuems that components cannot and will not be shared between entities.
for^ ent.components.entries {
+++ /dev/null
-
-use package core
-use package glfw3
-
-Player_Controls :: struct {
- up : i32;
- down : i32;
- left : i32;
- right : i32;
- interact : i32;
- pick_up : i32;
-}
-
-player_1_controls :: Player_Controls.{
- 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_2_controls :: Player_Controls.{
- up = GLFW_KEY_UP,
- down = GLFW_KEY_DOWN,
- left = GLFW_KEY_LEFT,
- right = GLFW_KEY_RIGHT,
- interact = GLFW_KEY_COMMA,
- pick_up = GLFW_KEY_PERIOD,
-}
-
-Facing :: enum {
- Up :: 0x01;
- Down :: 0x02;
- Left :: 0x03;
- Right :: 0x04;
-}
-
-facing_to_direction_vector :: macro (f: Facing) -> Vector2 {
- switch f {
- case .Up do return .{0, -1};
- case .Down do return .{0, 1};
- case .Left do return .{-1, 0};
- case .Right do return .{1, 0};
- }
-}
-
-#local Player :: struct {
- #struct_tag Entity_Schematic.{create}
- create :: (scene: ^Entity_Manager) => player_create(scene, .{0,0});
-}
-
-player_create :: (scene: ^Entity_Manager, pos: Vector2, controls := player_1_controls) -> ^Entity {
- this := scene->make();
- this.pos = pos;
- this.size = .{32, 32};
- this.flags |= .Solid;
-
- scene->create_and_add(this, MovementComponent) {
- comp.controls = controls;
- }
-
- scene->create_and_add(this, PlayerComponent) {
- comp.holding = Entity_Nothing;
- }
-
- scene->create_and_add(this, RenderComponent) {
- comp.func = PlayerComponent.render;
- comp.color = .{1,1,1};
- }
-
- return this;
-}
-
-MovementComponent :: struct {
- use component: Component;
- controls : Player_Controls;
- facing := Facing.Up;
-
- update :: (movement: ^MovementComponent, use this: ^Entity, dt: f32) {
- speed :: 128.0f;
-
- delta: Vector2;
- if is_key_down(movement.controls.left) { delta.x -= speed * dt; movement.facing = .Left; }
- if is_key_down(movement.controls.right) { delta.x += speed * dt; movement.facing = .Right; }
- if is_key_down(movement.controls.up) { delta.y -= speed * dt; movement.facing = .Up; }
- if is_key_down(movement.controls.down) { delta.y += speed * dt; movement.facing = .Down; }
-
- dist := math.max(size.x, size.y) * 2;
- walls := scene->query_by_flags(.{pos.x - dist, pos.y - dist, dist * 2, dist * 2}, .Solid);
- defer memory.free_slice(^walls);
-
- try_move(this, .{delta.x, 0}, walls);
- try_move(this, .{0, delta.y}, walls);
- }
-
- try_move :: (use this: ^Entity, delta: Vector2, obsticles: [] ^Entity) {
- pos += delta;
- ent_rect := Entity.get_rect(this);
-
- holding: Entity_ID;
- if player := this->get(PlayerComponent); player != null {
- holding = player.holding;
- }
-
- for obsticles {
- // Don't check collision on the object that you are carrying or yourself because it probably won't make sense.
- if it == this do continue;
- if it.id == holding do continue;
-
- if Rect.intersects(ent_rect, Entity.get_rect(it)) {
- pos -= delta;
- break;
- }
- }
- }
-}
-
-PlayerComponent :: struct {
- use base: Component;
-
- holding : Entity_ID;
-
- update :: (player: ^PlayerComponent, use this: ^Entity, dt: f32) {
- // Highlight the object that you are going to interact with
-
- movement := this->get(MovementComponent);
-
- //
- // Try to interact with nearby objects
- //
- if is_key_just_up(movement.controls.interact) {
- area := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
- objects := scene->query_by_component(area, InteractableComponent);
- defer memory.free_slice(^objects);
-
- for objects {
- interact_comp := it->get(InteractableComponent);
- interact_comp.interact(it, this);
- }
- }
-
- //
- // Try to pick up nearby objects
- //
- if is_key_just_up(movement.controls.pick_up) {
- if player.holding != Entity_Nothing {
- holding_object := scene->get(player.holding);
- holding_object.flags |= .Carryable;
- r := Entity.get_rect(holding_object);
-
- d := Vector2.{(size.x + r.w) / 2 + 4, (size.y + r.h) / 2 + 4};
- holding_object.pos = pos + facing_to_direction_vector(movement.facing) * d;
- player.holding = Entity_Nothing;
-
- } else {
- area := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
- objects := scene->query_by_flags(area, .Carryable);
- defer memory.free_slice(^objects);
-
- // This should first sort by the distance to the object
- while i := 0; i < objects.count {
- defer i += 1;
- if objects[i] == this do continue;
-
- target_object := objects[i];
- target_object.flags ^= .Carryable; // This only works because we assume that it is Carryable, otherwise this would make it carryable.
- player.holding = target_object.id;
- break;
- }
-
- // Should this fire an event on the target object?
- }
- }
-
- //
- // Adjust the position of the entity you are carrying
- // to be above the players head.
- //
- if player.holding != Entity_Nothing {
- holding_object := scene->get(player.holding);
- r := Entity.get_rect(holding_object);
- holding_object.pos = pos - .{0, (size.y + r.h) / 2};
- }
- }
-
- render :: (use this: ^Entity) {
- rect := Entity.get_rect(this);
- immediate_image(^player_assets.texture, rect.x, rect.y, rect.w, rect.h);
- }
-}
-
-#local Wall :: struct {
- render :: (use this: ^Entity) {
- r := Entity.get_rect(this);
- immediate_rectangle(r.x, r.y, r.w, r.h);
- }
-
- #struct_tag Entity_Schematic.{
- (scene) => wall_create(scene, .{0,0}, .{0,0})
- }
-}
-
-wall_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity {
- this := scene->make();
- this.pos = pos;
- this.size = size;
- this.flags |= .Solid;
-
- scene->create_and_add(this, RenderComponent) {
- comp.func = Wall.render;
- }
-
- return this;
-}
-
-player_assets: struct {
- #tag "assets/images/player.png"
- #tag Texture_Wrap.Clamp
- texture: Texture;
-}
-
-#local Door :: struct {
- create :: (scene) => door_create(scene, .Zero, .Zero);
-
- render :: (use this: ^Entity) {
- immediate_set_color(.{0.7, 0.7, 0.1});
-
- r := Entity.get_rect(this);
- immediate_rectangle(r.x, r.y, r.w, r.h);
- }
-
- get_rect :: (use this: ^Entity) => {
- door := this->get(DoorComponent);
- size_x := size.x * (1 - door.openness);
- return Rect.{ pos.x - size_x / 2, pos.y - size.y / 2, size_x, size.y };
- }
-
- interact :: (use this: ^Entity, interactor: ^Entity) {
- // Doors only open if interacted with by a player
- if interactor->has(PlayerComponent) {
- door := this->get(DoorComponent);
- door->toggle_open();
- }
- }
-
- #struct_tag Entity_Schematic.{create}
-}
-
-DoorComponent :: struct {
- use base: Component;
-
- max_openness := 0.8f;
- target_openness := 0.0f;
- openness := 0.0f;
-
- update :: (use this: ^DoorComponent, door: ^Entity, dt: f32) {
- if openness != target_openness {
- move_towards(^openness, target_openness, dt * 2);
- }
- }
-
- toggle_open :: (use this: ^DoorComponent) {
- target_openness = max_openness - target_openness;
- }
-}
-
-door_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity {
- this := scene->make();
- this.pos = pos;
- this.size = size;
-
- this.flags |= .Interactable;
- this.flags |= .Solid;
-
- scene->create_and_add(this, DoorComponent) {}
- scene->create_and_add(this, RenderComponent) { comp.func = Door.render; }
- scene->create_and_add(this, SizeComponent) { comp.func = Door.get_rect; }
- scene->create_and_add(this, InteractableComponent) { comp.interact = Door.interact; }
-
- return this;
-}
-
-
-
-@Relocate
-move_towards :: (v: ^$T, target: T, diff: T) {
- if math.abs(target - *v) <= diff {
- *v = target;
- return;
- }
-
- if *v < target do *v += diff;
- if *v > target do *v -= diff;
-}
--- /dev/null
+
+use package core
+use package glfw3
+
+Player_Controls :: struct {
+ up : i32;
+ down : i32;
+ left : i32;
+ right : i32;
+ interact : i32;
+ pick_up : i32;
+}
+
+player_1_controls :: Player_Controls.{
+ 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_2_controls :: Player_Controls.{
+ up = GLFW_KEY_UP,
+ down = GLFW_KEY_DOWN,
+ left = GLFW_KEY_LEFT,
+ right = GLFW_KEY_RIGHT,
+ interact = GLFW_KEY_COMMA,
+ pick_up = GLFW_KEY_PERIOD,
+}
+
+
+Player :: struct {
+ #struct_tag Entity_Schematic.{
+ (scene: ^Entity_Manager) => Player.create(scene, .{0,0})
+ }
+
+ create :: (scene: ^Entity_Manager, pos: Vector2, controls := player_1_controls) -> ^Entity {
+ this := scene->make();
+ this.pos = pos;
+ this.size = .{32, 32};
+ this.flags |= .Solid;
+
+ scene->create_and_add(this, MovementComponent) {
+ comp.controls = controls;
+ }
+
+ scene->create_and_add(this, PlayerComponent) {
+ comp.holding = Entity_Nothing;
+ }
+
+ scene->create_and_add(this, RenderComponent) {
+ comp.func = PlayerComponent.render;
+ comp.color = .{1,1,1};
+ }
+
+ return this;
+ }
+
+ #persist assets: struct {
+ #tag "assets/images/player.png"
+ #tag Texture_Wrap.Clamp
+ texture: Texture;
+ }
+}
+
+
defer os.close(^input_file);
reader := io.reader_make(^input_file);
- for entities do this->delete(it);
- map.clear(^entity_map);
+ for entities do this->delete(it, false);
array.clear(^entities);
+ map.clear(^entity_map);
next_entity_id = 0;
use type_info;
// This process of queueing the asset bucket should
// be made automatic somehow...
- queue_assets(^player_assets);
+ queue_assets(^Player.assets);
load_assets();