#load "entity/editor"
#load "entity/manager"
#load "entity/player"
+#load "entity/store"
#load "gfx/font"
#load "gfx/immediate"
entity_type := ^scene.entity_types.entries[active_index].value;
entity := entity_type.create_default(^scene);
+ scene->add(entity);
entity.pos = mouse_pos;
}
}
#local handle_entity_selction_and_dragging :: (dt: f32) {
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 {
- if active_tab == .Edit do active_index = -1;
- selected_entity_id = Entity_Nothing;
+ if mouse_pos.x < ~~window_width - sidebar_width && mouse_pos.y > menubar_height {
+ 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;
- for scene.entities {
- get_rect := scene.entity_types[it.type].get_rect;
- if get_rect == null_proc do continue;
+ for scene.entities {
+ get_rect := scene.entity_types[it.type].get_rect;
+ if get_rect == null_proc do continue;
- if get_rect(it) |> Rect.contains(mouse_pos) {
- selected_entity_id = it.id;
- break;
+ if get_rect(it) |> Rect.contains(mouse_pos) {
+ selected_entity_id = it.id;
+ break;
+ }
}
+
+ dragging = is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) && selected_entity_id != Entity_Nothing;
+ resizing = is_button_just_down(GLFW_MOUSE_BUTTON_RIGHT) && selected_entity_id != Entity_Nothing;
}
-
- dragging = selected_entity_id != Entity_Nothing;
}
if !is_button_down(GLFW_MOUSE_BUTTON_LEFT) {
dragging = false;
}
+ if !is_button_down(GLFW_MOUSE_BUTTON_RIGHT) {
+ resizing = false;
+ }
+
if selected_entity_id != Entity_Nothing && active_tab == .Edit {
selected_entity := scene->get(selected_entity_id);
if is_key_down(GLFW_KEY_LEFT) do selected_entity.pos.x -= 32 * dt;
if is_key_down(GLFW_KEY_RIGHT) do selected_entity.pos.x += 32 * dt;
}
+
+ if resizing {
+ selected_entity.size += mouse_get_delta_vector();
+ }
}
}
selected_entity_id: Entity_ID;
dragging := false;
+ resizing := false;
sidebar_width := 0.0f;
menubar_height := 40.0f;
#operator == (a, b: Entity_ID) => cast(u32) a == cast(u32) b;
#operator != (a, b: Entity_ID) => cast(u32) a != cast(u32) b;
+#init #after conv.custom_formatters_initialized () {
+ conv.register_custom_parser((e: ^Entity_ID, line: str, _: Allocator) => {
+ *e = ~~ cast(u32) conv.str_to_i64(line);
+ return true;
+ });
+}
+
Entity :: struct {
id: Entity_ID;
+ [Entity_Store.Skip]
type: type_expr;
flags: Entity_Flags;
- pos: Vector2;
+ pos: Vector2;
+ size: Vector2;
+
+ get_rect :: (use e: ^Entity) => Rect.{ pos.x - size.x / 2, pos.y - size.y / 2, size.x, size.y };
}
Entity_Flags :: enum #flags {
// These have defined values for stable serialization.
-
Interactable :: 0x01;
Solid :: 0x02;
Carryable :: 0x04;
}
}
-entity_manager_add :: (use this: ^Entity_Manager, entity: ^$T) -> Entity_ID where IsEntity(^T) {
- this->register(T);
+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.
+ if entity.id == 0 {
+ entity.id = next_entity_id;
+ next_entity_id = ~~(cast(u32) next_entity_id + 1);
+ } else {
+ next_entity_id = ~~(math.max(cast(u32) next_entity_id + 1, cast(u32) entity.id));
+ }
- entity.type = T;
- entity.id = next_entity_id;
- next_entity_id = ~~(cast(u32) next_entity_id + 1);
+ assert(cast(u32) entity.type != 0, "Adding an entity without a type!");
entities << entity;
entity_map[entity.id] = entity;
#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.{});
- this->add(entity);
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;
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};
+ }
+}
+
Player :: struct {
use entity: Entity;
holding: Entity_ID;
controls: Player_Controls;
color := Color.{1,1,1};
-
- Size :: 16.0f
+ facing := Facing.Up;
init_data :: struct {
pos := Vector2.{400, 200};
init :: (use this: ^Player, data: init_data) {
this.pos = data.pos;
+ this.size = .{32, 32};
this.holding = Entity_Nothing;
this.controls = data.controls;
this.flags |= .Solid;
}
- get_rect :: (use this: ^Player) => Rect.{ pos.x - Size, pos.y - Size, Size * 2, Size * 2 };
+ get_rect :: Entity.get_rect
update :: (use this: ^Player, dt: f32) {
- speed :: Size * 8;
+ speed :: 128.0f;
delta: Vector2;
- 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;
+ if is_key_down(controls.left) { delta.x -= speed * dt; facing = .Left; }
+ if is_key_down(controls.right) { delta.x += speed * dt; facing = .Right; }
+ if is_key_down(controls.up) { delta.y -= speed * dt; facing = .Up; }
+ if is_key_down(controls.down) { delta.y += speed * dt; facing = .Down; }
- walls := scene->query_by_flags(.{pos.x - 100, pos.y - 100, 200, 200}, .Solid);
+ 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);
vtable := ^scene.entity_types[holding_object.type];
if vtable.get_rect != null_proc {
r := vtable.get_rect(holding_object);
- holding_object.pos = pos + .{Size + r.w / 2 + 4, 0};
+
+ 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 {
- area := Rect.{pos.x - Size * 2, pos.y - Size * 2, Size * 4, Size * 4};
+ 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);
// 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};
+ area := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
objects := scene->query_by_flags(area, .Interactable);
defer memory.free_slice(^objects);
vtable := ^scene.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 / 2};
+ holding_object.pos = pos - .{0, (size.y + r.h) / 2};
}
}
}
Wall :: struct {
use entity: Entity;
- size: Vector2;
init_data :: struct {
pos := Vector2.{0, 0};
flags |= .Solid;
}
- get_rect :: (use this: ^Wall) => Rect.{ pos.x - size.x / 2, pos.y - size.y / 2, size.x, size.y };
+ get_rect :: Entity.get_rect
draw :: (use this: ^Wall) {
immediate_set_color(.{1,1,1});
Door :: struct {
use entity: Entity;
- size: Vector2;
- openness := 0.0f;
+ [Entity_Store.Skip] openness := 0.0f;
target_openness := 0.0f;
init_data :: struct {
Item :: struct {
use entity: Entity;
- Size :: 8.0f;
-
color: Color;
init_data :: struct {
init :: (use this: ^Item, data: init_data) {
this.pos = data.pos;
+ this.size = .{16, 16};
this.color = data.color;
this.flags |= .Carryable;
}
- get_rect :: (use this: ^Item) => Rect.{ pos.x - Size, pos.y - Size, Size * 2, Size * 2 };
+ get_rect :: Entity.get_rect
draw :: (use this: ^Item) {
immediate_set_color(this.color);
--- /dev/null
+
+use package core
+
+// Flags to control how a field of a structure is being stored
+// in the level file.
+Entity_Store :: enum {
+ Skip;
+}
+
+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);
+ writer := ^writer_;
+
+ // writer := ^stdio.print_writer;
+
+ use type_info;
+
+ for entities {
+ info := cast(^Type_Info_Struct) get_type_info(it.type);
+ io.write_format(writer, ":{}\n", info.name);
+ emit_struct_fields(any.{~~ it, it.type}, writer, "");
+ io.write(writer, "\n");
+ }
+
+ emit_struct_fields :: (v: any, dest: ^io.Writer, parent_name: str) {
+ use type_info;
+
+ info := cast(^Type_Info_Struct) get_type_info(v.type);
+ assert(info.kind == .Struct, "Expected type to be a structure.");
+
+ name_buffer: [1024] u8;
+ output_buffer: [2048] u8;
+ for info.members {
+ for it.tags {
+ if it.type != Entity_Store do continue;
+ if *(cast(^Entity_Store) it.data) == .Skip do continue continue;
+ }
+
+ member_info := get_type_info(it.type);
+ member_any := any.{cast(^u8) v.data + it.offset, it.type};
+
+ name := conv.format(name_buffer, "{}{}{}",
+ parent_name,
+ "." if parent_name.count > 0 else "",
+ it.name);
+
+ switch member_info.kind {
+ case .Struct {
+ emit_struct_fields(member_any, dest, name);
+ }
+
+ case .Basic, .Distinct, .Enum {
+ output := conv.format_va(output_buffer, "{} = {d}\n", .[.{^name, str}, member_any]);
+ io.write(dest, output);
+ }
+
+ case #default {
+ msg := conv.format(output_buffer, "Unhandled output case: {}\n", it.type);
+ // assert(false, msg);
+ printf("ERROR: {}\n", msg);
+ }
+ }
+ }
+ }
+}
+
+entity_manager_load_from_file :: (use this: ^Entity_Manager, filename: str) {
+ err, input_file := os.open(filename, .Read);
+ defer os.close(^input_file);
+ reader := io.reader_make(^input_file);
+
+ use type_info;
+
+ current_entity: ^Entity;
+
+ line_number := 1;
+ while !io.reader_empty(^reader) {
+ defer line_number += 1;
+
+ line := io.read_line(^reader, inplace=true);
+ if line.count <= 1 do continue;
+
+ if line[0] == #char ":" {
+ if current_entity != null do this->add(current_entity);
+
+ entity_kind := string.advance(line)
+ |> string.strip_whitespace();
+
+ entity_info : ^Entity_Info;
+ for^ entity_types.entries {
+ if (cast(^Type_Info_Struct) get_type_info(it.key)).name == entity_kind {
+ entity_info = ^it.value;
+ break;
+ }
+ }
+
+ current_entity = entity_info.create_default(this);
+ continue;
+ }
+
+ var_name := string.read_until(^line, #char "=")
+ |> string.strip_whitespace();
+ string.advance(^line, 1);
+ string.strip_whitespace(^line);
+
+ member := get_any_for_member(any.{current_entity, current_entity.type}, var_name);
+ if !conv.parse_any(member.data, member.type, line) {
+ printf("Unable to parse '{}' for type '{}' on line {}.\n", line, member.type, line_number);
+ }
+ }
+
+ if current_entity != null do this->add(current_entity);
+
+ get_any_for_member :: (base: any, member_name: str) -> any {
+ use type_info;
+
+ name := member_name;
+ info := cast(^Type_Info_Struct) get_type_info(base.type);
+ assert(info.kind == .Struct, "bad type");
+
+ part_name := string.read_until(^name, #char ".");
+ string.advance(^name, 1);
+
+ for info.members {
+ if it.name == part_name {
+ member_info := get_type_info(it.type);
+ if member_info.kind == .Struct {
+ return get_any_for_member(any.{cast(^u8) base.data + it.offset, it.type}, name);
+ } else {
+ return any.{cast(^u8) base.data + it.offset, it.type};
+ }
+ }
+ }
+ }
+}
+
+
+#local {
+
+}
vertex_count += 6;
}
+immediate_subimage :: (image: ^Texture, x, y, w, h: f32, tx, ty, tw, th: f32) {
+ if vertex_count > 0 do immediate_flush();
+
+ set_rendering_type(.Image);
+ texture_use(image);
+ shader_use(imgui_shader);
+ shader_set_uniform(imgui_shader, #cstr "u_texture", 0);
+
+ sx := tx / ~~ image.width;
+ sy := ty / ~~ image.height;
+ sw := tw / ~~ image.width;
+ sh := th / ~~ image.height;
+
+ vertex_data[vertex_count + 0] = .{ .{x, y}, .{sx, sy}, immediate_color };
+ vertex_data[vertex_count + 1] = .{ .{x+w, y}, .{sx+sw, sy}, immediate_color };
+ vertex_data[vertex_count + 2] = .{ .{x+w, y+h}, .{sx+sw,sy+sh}, immediate_color };
+ vertex_data[vertex_count + 3] = .{ .{x, y}, .{sx, sy}, immediate_color };
+ vertex_data[vertex_count + 4] = .{ .{x+w, y+h}, .{sx+sw,sy+sh}, immediate_color };
+ vertex_data[vertex_count + 5] = .{ .{x, y+h}, .{sx, sy+sh}, immediate_color };
+ vertex_count += 6;
+}
+
immediate_ellipse :: () {}
Color :: struct {
#if DEBUG { debug_font = font_lookup(.{"./assets/fonts/calibri.ttf", 16}); }
scene = entity_manager_create();
- scene->make(Player, .{ pos = .{300, 300}, controls=player_1_controls });
- scene->make(Player, .{ pos = .{400, 300}, controls=player_2_controls, color=.{1,0,0} });
-
- scene->make(Wall, .{ .{100, 100}, .{400, 50} });
- // scene->make(Wall, .{ .{100, 100}, .{50, 400} });
- // scene->make(Wall, .{ .{450, 100}, .{50, 400} });
- // scene->make(Wall, .{ .{100, 450}, .{400, 50} });
-
- scene->make(Door, .{ .{150, 150}, .{50, 50} });
- scene->make(Door, .{ .{400, 150}, .{50, 50} });
- scene->make(Door, .{ .{400, 400}, .{50, 50} });
- scene->make(Door, .{ .{150, 400}, .{50, 50} });
-
- scene->make(Item, .{ pos=.{ 250, 250 }, color=.{1,0,0} });
- scene->make(Item, .{ pos=.{ 275, 250 }, color=.{0,1,0} });
- scene->make(Item, .{ pos=.{ 300, 250 }, color=.{0,0,1} });
+ scene->register(Player);
+ scene->register(Wall);
+ scene->register(Door);
+ scene->register(Item);
+ entity_manager_load_from_file(^scene, "./Asdf");
+
+ // scene->make(Player, .{ pos = .{300, 300}, controls=player_1_controls });
+ // scene->make(Player, .{ pos = .{400, 300}, controls=player_2_controls, color=.{1,0,0} });
#if DEBUG {
println("Registered Entity types:");
editor_toggle();
}
+ if is_key_just_up(GLFW_KEY_F8) {
+ entity_manager_save_to_file(^scene, "Asdf");
+ }
+
if editor_shown() {
editor_update(dt);
return;
#operator + macro (v1, v2: Vector2) => (typeof v1).{ v1.x + v2.x, v1.y + v2.y };
#operator - macro (v1, v2: Vector2) => (typeof v1).{ v1.x - v2.x, v1.y - v2.y };
#operator * macro (v: Vector2, s: f32) => (typeof v1).{ v.x * s, v.y * s };
+#operator * macro (v1, v2: Vector2) => (typeof v1).{ v1.x * v2.x, v1.y * v2.y };
#operator == macro (v1, v2: Vector2) => v1.x == v2.x && v1.y == v2.y;
#operator + macro (v1, v2: Vector3) => Vector3.{ v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };
#operator - macro (v1, v2: Vector3) => Vector3.{ v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
#operator * macro (v: Vector3, s: f32) => Vector3.{ v.x * s, v.y * s, v.z * s };
+#operator * macro (v1, v2: Vector3) => (typeof v1).{ v1.x * v2.x, v1.y * v2.y, v1.z * v3.z };
#operator == macro (v1, v2: Vector3) => v1.x == v2.x && v1.y == v2.y && v1.z == v2.z;
#operator + macro (v1, v2: Vector3i) => Vector3i.{ v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };
#operator - macro (v1, v2: Vector3i) => Vector3i.{ v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
#operator * macro (v: Vector3i, s: i32) => Vector3i.{ v.x * s, v.y * s, v.z * s };
+#operator * macro (v1, v2: Vector3i) => (typeof v1).{ v1.x * v2.x, v1.y * v2.y, v1.z * v3.z };
#operator == macro (v1, v2: Vector3i) => v1.x == v2.x && v1.y == v2.y && v1.z == v2.z;
#local {