:beer
name="Beer"
-texture_path="./assets/images/beer-1.png"
+texture_path="./assets/images/spritesheet.png"
texture_pos = 0 0
texture_size = 16 16
[Player]
id = 12
flags = 2
-pos.x = 379.7975
+pos.x = 379.7973
pos.y = 175.1558
size.x = 32.0000
size.y = 32.0000
[Patreon]
id = 22
flags = 3
-pos.x = 385.0000
-pos.y = 248.0000
+pos.x = 388.6049
+pos.y = 284.5303
size.x = 32.0000
size.y = 32.0000
:RenderComponent
color.a = 1.0000
:PatreonComponent
state = 0
+seat_location.x = 400.0000
+seat_location.y = 400.0000
order_item = "beer"
+order_show_animation = 0.0000
holding = 0
consume_timeout = 0.0000
item = "beer"
max_timeout = 2.0000
-[Item_Entity]
-id = 21
-flags = 4
-pos.x = 187.7317
-pos.y = 106.9191
-size.x = 16.0000
-size.y = 16.0000
-:RenderComponent
-layer = 10
-color.r = 1.0000
-color.g = 1.0000
-color.b = 1.0000
-color.a = 1.0000
-:ItemComponent
-item = "beer"
-
}
// This should dynamic...
+ item.pos = this.pos;
item.size = .{16, 16};
(item->get(ItemComponent)).item = dispenser_comp.item;
use package core
#local Patreon_State :: enum {
+ Walking_To_Seat;
Waiting_To_Place_Order;
Waiting_For_Order;
Consuming_Order;
use component: Component;
state := Patreon_State.Waiting_To_Place_Order;
+ seat_location: Vector2;
order_item: str;
+ order_show_animation: f32;
holding: Entity_ID;
consume_timeout: f32;
init :: (use this: ^PatreonComponent) {
order_item = "beer";
+ seat_location = .{ 400, 400 };
}
added :: (use this: ^PatreonComponent, entity: ^Entity) {
}
update :: (use this: ^PatreonComponent, entity: ^Entity, dt: f32) {
+ if state == .Walking_To_Seat {
+ delta := Vector2.norm(seat_location - entity.pos) * 100;
+ entity.pos += delta * dt;
+
+ if Vector2.square_mag(seat_location - entity.pos) < 16 {
+ entity.pos = seat_location;
+ state = .Waiting_To_Place_Order;
+ }
+ }
+
if consume_timeout > 0 && state == .Consuming_Order {
consume_timeout -= dt;
if consume_timeout < 0 {
}
}
+ if state == .Waiting_For_Order {
+ if order_show_animation > 0 {
+ order_show_animation *= 0.75;
+ }
+ }
+
if holding != Entity_Nothing {
holding_object := scene->get(holding);
r := Entity.get_rect(holding_object);
- holding_object.pos = entity.pos + .{-10, 0};
+ target_pos := entity.pos + .{-10, 0};
+ holding_object.pos += (target_pos - holding_object.pos) * 0.25;
}
}
case .Waiting_To_Place_Order {
if interactor->has(PlayerComponent) {
patreon.state = .Waiting_For_Order;
+ patreon.order_show_animation = 1.0f;
}
}
}
post_render :: (use this: ^PatreonComponent, entity: ^Entity) {
- if state == .Waiting_For_Order {
- r := entity->get_rect();
+ r := entity->get_rect();
- r = Rect.{ r.x + (r.w - 24) / 2, r.y - r.h / 2 - 16, 24, 24 };
+ r = Rect.{ r.x + (r.w - 24) / 2, r.y - r.h / 2 - 16, 24, 24 };
+ r.y += 24 * order_show_animation;
+
+ if state == .Waiting_To_Place_Order {
+ immediate_set_color(.{1, 1, 1});
+ immediate_subimage(^Spritesheet, r.x + 4, r.y + 4, 16, 16, 1*16, 4*16, 16, 16);
+ }
+
+ if state == .Waiting_For_Order {
immediate_set_color(.{0, 0, 0});
immediate_rectangle(r.x, r.y, r.w, r.h);
holding : Entity_ID;
+ #tag Editor_Hidden, Entity_Store.Skip
+ nearby_holding, nearby_interact : Entity_ID;
+
update :: (player: ^PlayerComponent, use this: ^Entity, dt: f32) {
// Highlight the object that you are going to interact with
+ {
+ player.nearby_interact = Entity_Nothing;
+ 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);
- movement := this->get(MovementComponent);
+ dist := size.x * size.x + size.y * size.y;
- // Try to interact with nearby objects
- //
- if is_key_just_up(movement.controls.interact) {
+ for objects {
+ d := Vector2.square_mag(pos - it.pos);
+ if d < dist {
+ player.nearby_interact = it.id;
+ dist = d;
+ }
+ }
+ }
+
+ {
+ player.nearby_holding = Entity_Nothing;
area := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
- objects := scene->query_by_component(area, InteractableComponent);
+ objects := scene->query_by_flags(area, .Carryable);
defer memory.free_slice(^objects);
+ dist := size.x * size.x + size.y * size.y;
+
for objects {
- interact_comp := it->get(InteractableComponent);
- interact_comp.interact(it, this);
+ d := Vector2.square_mag(pos - it.pos);
+ if d < dist {
+ player.nearby_holding = it.id;
+ dist = d;
+ }
}
}
+ movement := this->get(MovementComponent);
+
+ // Try to interact with nearby objects
+ //
+ if is_key_just_up(movement.controls.interact) && player.nearby_interact != Entity_Nothing {
+ it := scene->get(player.nearby_interact);
+ interact_comp := it->get(InteractableComponent);
+ interact_comp.interact(it, this);
+ }
+
//
// Try to pick up nearby objects
//
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?
+ } elseif player.nearby_holding != Entity_Nothing {
+ target_object := scene->get(player.nearby_holding);
+ 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;
}
}
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};
+ target_pos := pos - .{0, (size.y + r.h) / 2};
+ holding_object.pos += (target_pos - holding_object.pos) * 0.25;
}
}
render :: (use this: ^Entity) {
rect := Entity.get_rect(this);
- immediate_image(^Player.assets.texture, rect.x, rect.y, rect.w, rect.h);
+ immediate_subimage(^Spritesheet, rect.x, rect.y, rect.w, rect.h, 0*16, 4*16, 16, 16);
+ }
+
+ post_render :: (use this: ^PlayerComponent, entity: ^Entity) {
+ if nearby_interact != Entity_Nothing {
+ it := scene->get(nearby_interact);
+ r := it->get_rect();
+ immediate_set_color(.{.2, .2, .8, .7});
+ immediate_rectangle(r.x, r.y, r.w, r.h);
+ }
+
+ if nearby_holding != Entity_Nothing && holding == Entity_Nothing {
+ it := scene->get(nearby_holding);
+ r := it->get_rect();
+ immediate_set_color(.{.2, .8, .2, .7});
+ immediate_rectangle(r.x, r.y, r.w, r.h);
+ }
}
}
if selected_entity_id != Entity_Nothing {
selected_entity := scene->get(selected_entity_id);
render_entity_fields(selected_entity, x, y, w, h);
+ } else {
+ render_entity_list(x, y, w, h);
}
}
}
draw_checkbox(.{x, y + 150, w, 36.0f}, ^dumb, "Testing checkboxes");
}
+#local render_entity_list :: (x, y, w, h: f32) {
+ buf: [256] u8;
+ for scene.entities {
+ msg := conv.format(buf, "{} ({})", it.schematic, cast(u32) it.id);
+ if draw_button(.{ x, y, w, 32 }, msg, increment=~~it) {
+ selected_entity_id = it.id;
+ }
+ y += 32;
+ }
+}
+
#local render_entity_fields :: (entity: ^Entity, x, y, w, h: f32) {
assert(entity != null, "entity is null");
y += 32;
for^ entry: entity.components.entries {
+ immediate_set_color(.{0, 0, 0});
+ immediate_rectangle(x, y, w, 4);
+
y += 16;
info = ~~ type_info.get_type_info(entry.key);
- font_print(editor_font, x + 2, y + 24, info.name);
+ font_print(editor_font, x + 2, y + 20, info.name);
y, i = render_struct_fields(any.{~~entry.value, entry.key}, i, x, y, w, h);
- y += 16;
+ y += 32;
}
}
immediate_rectangle(x, y + 2, w, Field_Height + 2);
}
- font_print(editor_font, x + 2, y + Field_Height, it.name);
+ font_print(editor_font, x + 2, y + Field_Height - 2, it.name);
#persist depth_colors := Color.[
Color.{0.4, 0.2, 0.2},
} else {
value_buf: [1024] u8;
value_str := conv.format_va(value_buf, "{}", .[member_any]);
- font_print(editor_font, x + w - font_get_width(editor_font, value_str) - 2, y + Field_Height, value_str);
+ font_print(editor_font, x + w - font_get_width(editor_font, value_str) - 2, y + Field_Height - 2, value_str);
}
}
}
#local {
- background_color :: Color.{0.5, 0.5, 0.5, 1};
+ background_color :: Color.{0.15, 0.15, 0.15, 1};
editor_font: Font;
editor_big_font: Font;
render :: (use this: ^Entity) {
r := this->get_rect();
- immediate_image(^Player.assets.texture, r.x, r.y, r.w, r.h);
+ immediate_subimage(^Spritesheet, r.x, r.y, r.w, r.h, 0*16, 4*16, 16, 16);
}
}
render :: (use this: ^Entity) {
rect := this->get_rect();
- immediate_image(^Tap.assets.texture, rect.x, rect.y, rect.w, rect.h);
- }
-
- #persist assets: struct {
- #tag "./assets/images/keg.png"
- texture: Texture;
+ immediate_subimage(^Spritesheet, rect.x, rect.y, rect.w, rect.h, 0*16, 2*16, 32, 32);
}
}
scene_render_offset: Vector2;
scene_canvas: Canvas;
+Spritesheet: Texture;
+
game_init :: () {
scene_canvas = canvas_make(800, 600);
// This process of queueing the asset bucket should
// be made automatic somehow...
- queue_assets(^Player.assets);
- queue_assets(^Tap.assets);
+ Spritesheet', _ := texture_lookup(#cstr "./assets/images/spritesheet.png");
load_assets();
x, y: f32;
mag :: macro (v: Vector2) => math.sqrt(v.x * v.x + v.y * v.y);
+
+ square_mag :: macro (v: Vector2) => v.x * v.x + v.y * v.y;
+
+ norm :: macro (v: Vector2) -> Vector2 {
+ l := math.sqrt(v.x * v.x + v.y * v.y);
+ return .{ v.x / l, v.y / l };
+ }
+
+
Zero :: Vector2.{0, 0}
#struct_tag conv.Custom_Format.{format_vector2}
#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 (v: Vector2, s: f32) => (typeof v ).{ 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;