diff --git a/paint/sources/traits/trait_point_and_click_controller.c b/paint/sources/traits/trait_point_and_click_controller.c index c1559606..68a993e6 100644 --- a/paint/sources/traits/trait_point_and_click_controller.c +++ b/paint/sources/traits/trait_point_and_click_controller.c @@ -1,7 +1,7 @@ #include "../global.h" -#define PAC_SPEED 1.0 +#define PAC_SPEED 0.4 #define PAC_ARRIVE 0.1 #define PAC_CLEARANCE 0.15 #define PAC_TERRAIN "Terrain" @@ -13,6 +13,7 @@ static char *trait_point_and_click_controller_object = NULL; static quat_t trait_point_and_click_controller_rot = {0.0, 0.0, 0.0, 1.0}; static bool trait_point_and_click_controller_moving = false; +static void *trait_point_and_click_controller_arrive = NULL; typedef struct { f32 min_x; @@ -34,6 +35,7 @@ void trait_point_and_click_controller_init(char *object) { trait_point_and_click_controller_object = string_copy(object); trait_point_and_click_controller_moving = false; trait_point_and_click_controller_rot = (quat_t){0.0, 0.0, 0.0, 1.0}; + trait_point_and_click_controller_arrive = NULL; pac_path_count = 0; pac_path_index = 0; } @@ -85,7 +87,7 @@ static void pac_gather_rects(physics_body_t *self) { } } -static vec4_t pac_push_out(vec4_t p) { +static vec4_t pac_push_out(vec4_t p, vec4_t ref) { for (i32 pass = 0; pass < 4; ++pass) { bool moved = false; for (i32 i = 0; i < pac_rect_count; ++i) { @@ -94,23 +96,24 @@ static vec4_t pac_push_out(vec4_t p) { continue; } - f32 left = p.x - r->min_x; - f32 right = r->max_x - p.x; - f32 down = p.y - r->min_y; - f32 up = r->max_y - p.y; - f32 best = math_min(math_min(left, right), math_min(down, up)); - if (best == left) { - p.x = r->min_x - PAC_EPS; - } - else if (best == right) { - p.x = r->max_x + PAC_EPS; - } - else if (best == down) { - p.y = r->min_y - PAC_EPS; - } - else { - p.y = r->max_y + PAC_EPS; + vec4_t exits[4] = { + {r->min_x - PAC_EPS, p.y, 0.0, 1.0}, + {r->max_x + PAC_EPS, p.y, 0.0, 1.0}, + {p.x, r->min_y - PAC_EPS, 0.0, 1.0}, + {p.x, r->max_y + PAC_EPS, 0.0, 1.0}, + }; + + i32 best = 0; + f32 best_dist = pac_dist(exits[0], ref); + for (i32 e = 1; e < 4; ++e) { + f32 dist = pac_dist(exits[e], ref); + if (dist < best_dist) { + best_dist = dist; + best = e; + } } + + p = exits[best]; moved = true; } if (!moved) { @@ -255,8 +258,9 @@ static bool pac_plan(physics_body_t *body, vec4_t goal) { physics_body_get_pos(body->_body, &pos); pac_gather_rects(body); - vec4_t start = pac_push_out((vec4_t){pos.x, pos.y, 0.0, 1.0}); - goal = pac_push_out((vec4_t){goal.x, goal.y, 0.0, 1.0}); + vec4_t here = (vec4_t){pos.x, pos.y, 0.0, 1.0}; + vec4_t start = pac_push_out(here, here); + goal = pac_push_out((vec4_t){goal.x, goal.y, 0.0, 1.0}, start); if (pac_clear(start, goal)) { pac_path[0] = goal; @@ -270,6 +274,24 @@ static bool pac_plan(physics_body_t *body, vec4_t goal) { return pac_search(); } +static void pac_trim(vec4_t from, f32 dist) { + if (dist <= 0.0 || pac_path_count == 0) { + return; + } + + vec4_t prev = pac_path_count > 1 ? pac_path[pac_path_count - 2] : from; + vec4_t goal = pac_path[pac_path_count - 1]; + f32 d = pac_dist(goal, prev); + if (d <= dist) { + pac_path_count--; + return; + } + + f32 t = (d - dist) / d; + pac_path[pac_path_count - 1].x = prev.x + (goal.x - prev.x) * t; + pac_path[pac_path_count - 1].y = prev.y + (goal.y - prev.y) * t; +} + static quat_t trait_point_and_click_controller_facing(vec4_t dir) { return quat_from_axis_angle(vec4_z_axis(), math_atan2(-dir.x, dir.y) + math_pi()); } @@ -386,9 +408,12 @@ void trait_point_and_click_controller_run() { } vec4_t target; - if (mouse_started("left") && trait_point_and_click_controller_pick(&target) && !pac_plan(body, target)) { - pac_path_count = 0; - pac_path_index = 0; + if (mouse_started("left") && trait_point_and_click_controller_pick(&target)) { + trait_point_and_click_controller_arrive = NULL; + if (!pac_plan(body, target)) { + pac_path_count = 0; + pac_path_index = 0; + } } bool moving = trait_point_and_click_controller_move(body); @@ -397,13 +422,48 @@ void trait_point_and_click_controller_run() { if (moving) { g_context->ddirty = 2; } + else if (trait_point_and_click_controller_arrive != NULL) { + void *fn = trait_point_and_click_controller_arrive; + trait_point_and_click_controller_arrive = NULL; + minic_call_fn(fn, NULL, 0); + } } void trait_point_and_click_controller_stop() { - pac_path_count = 0; - pac_path_index = 0; - object_t *o = script_get_object(trait_point_and_click_controller_object); + trait_point_and_click_controller_arrive = NULL; + pac_path_count = 0; + pac_path_index = 0; + object_t *o = script_get_object(trait_point_and_click_controller_object); if (o != NULL) { trait_point_and_click_controller_set_moving(o, false); } } + +void trait_point_and_click_controller_walk_to(f32 x, f32 y, void *on_arrive) { + trait_point_and_click_controller_arrive = NULL; + if (trait_point_and_click_controller_object == NULL) { + return; + } + + object_t *o = script_get_object(trait_point_and_click_controller_object); + if (o == NULL) { + return; + } + physics_body_t *body = trait_point_and_click_controller_body(o); + if (body == NULL) { + return; + } + + if (!pac_plan(body, (vec4_t){x, y, 0.0, 1.0})) { + pac_path_count = 0; + pac_path_index = 0; + return; + } + + vec4_t pos; + physics_body_get_pos(body->_body, &pos); + f32 stop_dist = 0.0; + pac_trim((vec4_t){pos.x, pos.y, 0.0, 1.0}, stop_dist); + + trait_point_and_click_controller_arrive = on_arrive; +}