mouse_pos := mouse_get_position_vector();
if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) && mouse_pos.x < ~~window_width - sidebar_width && mouse_pos.y > menubar_height {
+ /*
entity_type := ^scene.entity_types.entries[active_index].value;
entity := entity_type.create_default(^scene);
scene->add(entity);
entity.pos = mouse_pos;
+ */
}
}
selected_entity_id = Entity_Nothing;
for scene.entities {
- if get_rect := ENT_INFO(it).get_rect; get_rect != null_proc {
- if get_rect(it) |> Rect.contains(mouse_pos) {
- selected_entity_id = it.id;
- break;
- }
+ if Entity.get_rect(it) |> Rect.contains(mouse_pos) {
+ selected_entity_id = it.id;
+ break;
}
}
new_top_left.x = editor_grid_size * math.floor(new_top_left.x / editor_grid_size);
new_top_left.y = editor_grid_size * math.floor(new_top_left.y / editor_grid_size);
- rect := ENT_INFO(selected_entity).get_rect(selected_entity);
+ rect := Entity.get_rect(selected_entity);
new_rect := Rect.{ new_top_left.x, new_top_left.y, rect.w, rect.h };
selected_entity.pos = Rect.center(new_rect);
if resizing {
if editor_grid_shown {
- rect := ENT_INFO(selected_entity).get_rect(selected_entity);
+ rect := Entity.get_rect(selected_entity);
new_size := mouse_get_position_vector() - Rect.top_left(rect);
new_size.x = editor_grid_size * math.floor(new_size.x / editor_grid_size);
new_size.y = editor_grid_size * math.floor(new_size.y / editor_grid_size);
if selected_entity_id != Entity_Nothing {
selected_entity := scene->get(selected_entity_id);
- get_rect := ENT_INFO(selected_entity).get_rect;
- r := get_rect(selected_entity);
+ r := Entity.get_rect(selected_entity);
immediate_set_color(.{1,1,0,0.5});
immediate_rectangle(r.x-2, r.y-2, r.w+4, r.h+4);
immediate_rectangle(r.x, r.y, r.w, r.h);
#local render_create_sidebar :: (x, y, w, h: f32) {
i := 0;
+ /*
for scene.entity_types.entries {
defer i += 1;
active_index = i;
}
}
+ */
#persist test : [..] u8;
if test.data == null {
#local render_entity_fields :: (entity: ^Entity, x, y, w, h: f32) {
assert(entity != null, "entity is null");
- assert(type_info.struct_inherits(entity.type, Entity), "entity is not an entity");
+ /*
info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(entity.type);
font_print(editor_big_font, x + 2, y + 24, info.name);
if active_index >= 0 do sidebar_width += w;
render_struct_fields(any.{~~entity, entity.type}, 0, x, y + 4, w, h);
+ */
}
#local render_struct_fields :: (v: any, i: i32, x, y, w, h: f32, depth := 0) -> (new_y: f32, new_i: i32) {
} elseif it.type == Entity_ID {
value_buf: [1024] u8;
- entity_type := (scene->get(*cast(^Entity_ID) member_any.data)).type; // Dereferencing null here
- value_str := conv.format_va(value_buf, "{} ({})", .[member_any, .{^entity_type, type_expr}]);
- font_print(editor_font, x + w - font_get_width(editor_font, value_str) - 2, y + Field_Height, value_str);
+ // entity_type := (scene->get(*cast(^Entity_ID) member_any.data)).type; // Dereferencing null here
+ // value_str := conv.format_va(value_buf, "{} ({})", .[member_any, .{^entity_type, type_expr}]);
+ // font_print(editor_font, x + w - font_get_width(editor_font, value_str) - 2, y + Field_Height, value_str);
} else {
value_buf: [1024] u8;
});
}
+Component :: struct {
+ type: type_expr;
+}
+
+IsComponent :: interface (c: $C) {
+ { c } -> ^Component;
+}
+
+UpdateComponent :: struct {
+ use base: Component;
+ func : (e: ^Entity, dt: f32) -> void;
+}
+
+RenderComponent :: struct {
+ use base: Component;
+ func : (e: ^Entity) -> void;
+}
+
Entity :: struct {
id: Entity_ID;
- [Entity_Store.Skip]
- type: type_expr;
flags: Entity_Flags;
pos: Vector2;
- size: Vector2;
+ // Does every entity need to have a size?
+ size: Vector2;
get_rect :: (use e: ^Entity) => Rect.{ pos.x - size.x / 2, pos.y - size.y / 2, size.x, size.y };
+
+ components: Map(type_expr, ^Component);
+ has :: (use this: ^Entity, component_type: type_expr) => components->has(component_type);
+ get :: (use this: ^Entity, $component_type: type_expr) => cast(^component_type) components[component_type];
+ add :: (use this: ^Entity, component: ^Component) => {
+ if components->has(component.type) do return;
+ components[component.type] = component;
+ }
}
Entity_Flags :: enum #flags {
Carryable :: 0x04;
}
-IsEntity :: interface (e: $E) {
- { e } -> ^Entity;
- e.init_data;
- e.init(e, e.init_data.{}); // This constraint ensures that everything in the initialization data is given a default value.
-}
-
-Entity_Info :: struct {
- create_default : (scene: ^Entity_Manager) -> ^Entity = null_proc;
-
- destroy : (entity: ^Entity) -> void = null_proc;
- update : (entity: ^Entity, dt: f32) -> void = null_proc;
- draw : (entity: ^Entity) -> void = null_proc;
- get_rect : (entity: ^Entity) -> Rect = null_proc;
- interact : (entity: ^Entity, interactor: ^Entity) -> void = null_proc;
-}
-
Entity_Manager :: struct {
entity_allocator: Allocator;
entities: [..] ^Entity;
entity_map: Map(Entity_ID, ^Entity);
- entity_types: Map(type_expr, Entity_Info);
+ // entity_types: Map(type_expr, Entity_Info);
next_entity_id: Entity_ID;
- register :: entity_manager_register;
add :: entity_manager_add;
make :: entity_manager_make;
update :: entity_manager_update;
get :: entity_manager_get;
delete :: entity_manager_delete;
query :: entity_manager_query;
- query_by_type :: entity_manager_query_by_type;
query_by_flags :: entity_manager_query_by_flags;
+ query_by_component :: entity_manager_query_by_component;
+ create_component :: entity_manager_create_component;
+ create_and_add :: entity_manager_create_and_add;
+
load_from_file :: entity_manager_load_from_file;
save_to_file :: entity_manager_save_to_file;
}
-// This assumes that the main entity manager is called "scene".
-ENT_INFO :: macro (e: ^Entity) => {
- @CompilerBug // Why does the following line break the program?
- // It's like its declaring a new null scene...?
- // scene :: scene;
-
- return ^scene.entity_types[e.type];
-}
-
entity_manager_create :: () -> Entity_Manager {
em: Entity_Manager;
em.entity_allocator = context.allocator; @TODO // Replace the allocator here.
array.init(^em.entities, 4);
- map.init(^em.entity_types);
// Entity ID 0 is reserved as a "empty / null" entity
em.next_entity_id = 1;
return em;
}
-entity_manager_register :: (use this: ^Entity_Manager, $entity_type: type_expr) where IsEntity(^entity_type) {
- if !entity_types->has(entity_type) {
-
- #if DEBUG { // Validate that the entity_type does not have any pointers.
- use type_info;
-
- info := cast (^Type_Info_Struct) get_type_info(entity_type);
- for info.members {
- member_info := get_type_info(it.type);
- is_pointer := member_info.kind == .Pointer;
- if member_info.kind == .Basic {
- if (cast(^Type_Info_Basic) member_info).basic_kind == .Rawptr {
- is_pointer = is_pointer;
- }
- }
-
- message_buf: [1024] u8;
- message := conv.format(message_buf, "Cannot have a pointer member on an Entity! '{}' is a pointer!", it.name);
- assert(!is_pointer, message);
- }
- }
-
-
- info := Entity_Info.{};
- info.create_default = #solidify entity_manager_create_default { entity_type=entity_type };
-
- @CompilerFeatures // Maybe there should be data stored in the Type_Info_Struct about
- // the functions/methods that are defined in the structs scope. I don't know if that
- // would be worthwhile or if it would just be bulking up the type info data.
- #if #defined(entity_type.destroy) { info.destroy = entity_type.destroy; if DEBUG do debug_log(.Debug, "{} has '{}'.", entity_type, "destroy"); }
- #if #defined(entity_type.update) { info.update = entity_type.update; if DEBUG do debug_log(.Debug, "{} has '{}'.", entity_type, "update"); }
- #if #defined(entity_type.draw) { info.draw = entity_type.draw; if DEBUG do debug_log(.Debug, "{} has '{}'.", entity_type, "draw"); }
- #if #defined(entity_type.get_rect) { info.get_rect = entity_type.get_rect; if DEBUG do debug_log(.Debug, "{} has '{}'.", entity_type, "get_rect"); }
- #if #defined(entity_type.interact) { info.interact = entity_type.interact; if DEBUG do debug_log(.Debug, "{} has '{}'.", entity_type, "interact"); }
-
- entity_types[entity_type] = info;
- }
-}
-
entity_manager_add :: (use this: ^Entity_Manager, entity: ^Entity) -> Entity_ID {
// Automatically assign an id if the entity does not have one.
// This will not be used when loading the entity from a file.
next_entity_id = ~~(math.max(cast(u32) next_entity_id, cast(u32) entity.id) + 1);
}
- assert(cast(u32) entity.type != 0, "Adding an entity without a type!");
-
entities << entity;
entity_map[entity.id] = entity;
return entity.id;
}
-#local entity_manager_create_default :: (use this: ^Entity_Manager, $entity_type: type_expr) -> ^entity_type where IsEntity(^entity_type) {
- entity := new(entity_type, allocator=entity_allocator);
- entity.type = entity_type;
- entity_type.init(entity, entity_type.init_data.{});
+entity_manager_make :: (use this: ^Entity_Manager) -> ^Entity {
+ entity := new(Entity, allocator=entity_allocator);
+ map.init(^entity.components);
return entity;
}
-entity_manager_make :: (use this: ^Entity_Manager, $entity_type: type_expr, data: entity_type.init_data = .{}) -> ^entity_type where IsEntity(^entity_type) {
- entity := new(entity_type, allocator=entity_allocator);
- entity.type = entity_type;
- entity_type.init(entity, data);
- this->add(entity);
- return entity;
+entity_manager_create_component :: (use this: ^Entity_Manager, $component_type: type_expr) -> ^component_type where IsComponent(^component_type) {
+ comp := new(component_type, allocator=entity_allocator);
+ comp.type = component_type;
+ return comp;
+}
+
+entity_manager_create_and_add :: macro (this: ^Entity_Manager, entity: ^Entity, $component_type: type_expr, init_block: Code) where IsComponent(^component_type) {
+ comp := new(component_type, allocator=this.entity_allocator);
+ comp.type = component_type;
+
+ #insert init_block;
+ entity->add(comp);
}
entity_manager_update :: (use this: ^Entity_Manager, dt: f32) {
for entities {
- update := entity_types[it.type].update;
- if update == null_proc do continue;
-
- update(it, dt);
+ update_comp := it->get(UpdateComponent);
+ if update_comp != null {
+ update_comp.func(it, dt);
+ }
}
}
entity_manager_draw :: (use this: ^Entity_Manager) {
// Entities should be sorted by z-order.
for entities {
- draw := entity_types[it.type].draw;
- if draw == null_proc do continue;
-
- draw(it);
+ render_comp := it->get(RenderComponent);
+ if render_comp != null {
+ render_comp.func(it);
+ }
}
}
entity_manager_delete :: (use this: ^Entity_Manager, ent: ^Entity) {
map.delete(^entity_map, ent.id);
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 {
+ if it.value != null {
+ raw_free(entity_allocator, it.value);
+ }
+ }
+
+ map.free(^ent.components);
raw_free(entity_allocator, ent);
}
entity_manager_query :: (use this: ^Entity_Manager, area: Rect) -> [] ^Entity {
ents: [..] ^Entity;
for entities {
- get_rect := entity_types[it.type].get_rect;
- if get_rect == null_proc do continue;
-
- if Rect.intersects(get_rect(it), area) {
+ if Rect.intersects(Entity.get_rect(it), area) {
ents << it;
}
}
return ents;
-
}
-entity_manager_query_by_type :: (use this: ^Entity_Manager, area: Rect, type: type_expr) -> [] ^Entity {
- #if DEBUG {
- use type_info;
- assert(struct_inherits(type, Entity), "Expected a type that inherits from Entity!");
- }
-
+entity_manager_query_by_component :: (use this: ^Entity_Manager, area: Rect, comp_type: type_expr) -> [] ^Entity {
ents: [..] ^Entity;
for entities {
- if it.type != type do continue;
-
- get_rect := entity_types[it.type].get_rect;
- if get_rect == null_proc do continue;
+ if !it.components->has(comp_type) do continue;
- if Rect.intersects(get_rect(it), area) {
+ if Rect.intersects(Entity.get_rect(it), area) {
ents << it;
}
}
for entities {
if ~~ it.flags & expected_flags != expected_flags do continue;
- get_rect := entity_types[it.type].get_rect;
- if get_rect == null_proc do continue;
-
- if Rect.intersects(get_rect(it), area) {
+ if Rect.intersects(Entity.get_rect(it), area) {
ents << it;
}
}
return ents;
}
-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) entity;
- #insert body;
- }
-}
}
}
-Player :: struct {
- use entity: Entity;
+player_create :: (scene: ^Entity_Manager, pos := Vector2.{400, 200}, controls := player_1_controls) -> ^Entity {
+ this := scene->make();
+ this.pos = pos;
+ this.size = .{32, 32};
+ this.flags |= .Solid;
+
+ scene->create_and_add(this, PlayerComponent) {
+ comp.holding = Entity_Nothing;
+ comp.controls = controls;
+ comp.color = .{1,1,1};
+ }
- [Editor_Disabled]
- holding: Entity_ID;
- controls: Player_Controls;
- color := Color.{1,1,1};
- facing := Facing.Up;
+ scene->create_and_add(this, UpdateComponent) {
+ comp.func = PlayerComponent.update;
+ }
- init_data :: struct {
- pos := Vector2.{400, 200};
- controls: Player_Controls = player_1_controls;
- color := Color.{1,1,1};
+ scene->create_and_add(this, RenderComponent) {
+ comp.func = PlayerComponent.render;
}
- init :: (use this: ^Player, data: init_data) {
- this.pos = data.pos;
- this.size = .{32, 32};
- this.holding = Entity_Nothing;
+ return this;
+}
+
+PlayerComponent :: struct {
+ use base: Component;
- this.controls = data.controls;
- this.color = data.color;
+ color := Color.{1,1,1};
+ holding : Entity_ID;
+ controls : Player_Controls;
+ facing := Facing.Up;
+ create :: (scene: ^Entity_Manager, pos := Vector2.{400, 200}, controls := player_1_controls) -> ^Entity {
+ this := scene->make();
+ this.pos = pos;
+ this.size = .{32, 32};
this.flags |= .Solid;
+
+ player_comp := scene->create_component(PlayerComponent);
+ player_comp.holding = Entity_Nothing;
+ player_comp.controls = controls;
+ player_comp.color = .{1,1,1};
+ this->add(player_comp);
+
+ update_comp := scene->create_component(UpdateComponent);
+ update_comp.func = update;
+ this->add(update_comp);
+
+ render_comp := scene->create_component(RenderComponent);
+ render_comp.func = render;
+ this->add(render_comp);
+
+ return this;
+ }
+
+ update :: (use this: ^Entity, dt: f32) {
+ speed :: 128.0f;
+
+ player := this->get(PlayerComponent);
+
+ delta: Vector2;
+ if is_key_down(player.controls.left) { delta.x -= speed * dt; player.facing = .Left; }
+ if is_key_down(player.controls.right) { delta.x += speed * dt; player.facing = .Right; }
+ if is_key_down(player.controls.up) { delta.y -= speed * dt; player.facing = .Up; }
+ if is_key_down(player.controls.down) { delta.y += speed * dt; player.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;
+ }
+ }
+ }
+
+ render :: (use this: ^Entity) {
+ player := this->get(PlayerComponent);
+ immediate_set_color(player.color);
+
+ rect := Entity.get_rect(this);
+ immediate_image(^player_assets.texture, rect.x, rect.y, rect.w, rect.h);
+ }
+}
+
+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_draw;
}
+ return this;
+}
+
+wall_draw :: (use this: ^Entity) {
+ immediate_set_color(.{1,1,1});
+
+ r := Entity.get_rect(this);
+ immediate_rectangle(r.x, r.y, r.w, r.h);
+}
+
+/*
get_rect :: Entity.get_rect
update :: (use this: ^Player, dt: f32) {
if holding != Entity_Nothing {
holding_object := scene->get(holding);
holding_object.flags |= .Carryable;
+ /* :ENT_INFO
obj_get_rect := ENT_INFO(holding_object).get_rect;
if obj_get_rect != null_proc {
r := 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(facing) * d;
}
+ */
holding = Entity_Nothing;
} else {
defer memory.free_slice(^objects);
for objects {
+ /* :ENT_INFO
if interact := ENT_INFO(it).interact; interact != null_proc {
interact(it, this);
}
+ */
}
}
//
if holding != Entity_Nothing {
holding_object := scene->get(holding);
+ /* :ENT_INFO
obj_get_rect := ENT_INFO(holding_object).get_rect;
if obj_get_rect != null_proc {
r := obj_get_rect(holding_object);
holding_object.pos = pos - .{0, (size.y + r.h) / 2};
}
+ */
}
}
if it == this do continue;
if it.id == this.holding do continue;
+ /* :ENT_INFO
vtable := ENT_INFO(it);
if Rect.intersects(ent_rect, vtable.get_rect(it)) {
pos -= delta;
break;
}
+ */
}
}
- draw :: (use this: ^Player) {
- immediate_set_color(color);
-
- rect := this->get_rect();
- immediate_image(^player_assets.texture, rect.x, rect.y, rect.w, rect.h);
- }
}
+*/
player_assets: struct {
["assets/images/player.png"] texture: Texture;
}
-
-Wall :: struct {
- use entity: Entity;
-
- init_data :: struct {
- pos := Vector2.{0, 0};
- size := Vector2.{10, 10};
- }
-
- init :: (use this: ^Wall, data: init_data) {
- this.pos = data.pos;
- this.size = data.size;
-
- flags |= .Solid;
- }
-
- get_rect :: Entity.get_rect
-
- draw :: (use this: ^Wall) {
- immediate_set_color(.{1,1,1});
-
- r := Wall.get_rect(this);
- immediate_rectangle(r.x, r.y, r.w, r.h);
- }
-}
-
Door :: struct {
use entity: Entity;
interact :: (use this: ^Door, interactor: ^Entity) {
// Doors only open if interacted with by a player
- if_entity_is(interactor, Player) {
+ //if_entity_is(interactor, Player) {
target_openness = 0.8f - target_openness;
- }
+ //}
}
draw :: (use this: ^Door) {
}
entity_manager_save_to_file :: (use this: ^Entity_Manager, filename: str) {
+ /*
err, output_file := os.open(filename, .Write);
defer os.close(^output_file);
writer_ := io.writer_make(^output_file);
}
}
}
+ */
}
entity_manager_load_from_file :: (use this: ^Entity_Manager, filename: str) {
+ /*
err, input_file := os.open(filename, .Read);
if err != .None {
debug_log(.Error, "Failed to open file: {}", filename);
}
if current_entity != null do this->add(current_entity);
+ */
}
load_assets();
scene = entity_manager_create();
- scene->register(Player);
- scene->register(Wall);
- scene->register(Door);
- scene->register(Item_Entity);
- scene->load_from_file("scenes/quick_save.scene");
+ // scene->load_from_file("scenes/quick_save.scene");
+ player := player_create(^scene);
+ scene->add(player);
+
+ wall := wall_create(^scene, .{0, 100}, .{300, 40});
+ scene->add(wall);
item_store = item_store_make();
item_store->load_items_from_file("scenes/default.items");
-
- #if DEBUG {
- debug_log(.Debug, "Registered Entity types:");
- for scene.entity_types.entries {
- info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(it.key);
- debug_log(.Debug, " {}", info.name);
- }
- }
}
#local quick_save_file := "scenes/quick_save.scene";