diff --git a/engine.h b/engine.h index 20c9413..71014a7 100644 --- a/engine.h +++ b/engine.h @@ -44,6 +44,7 @@ struct object *find_object_by_id(int id); struct script *scene_add_script(int id, bool interrupt_existing); struct script *scene_get_script(int id); bool deliver_script_wakeup(int wakeupMode, int wakeupArg1, int wakeupType, int arg1, int arg2, int arg3, int arg4); // deliver to scripts with given wakeupMode and wakeupArg1. Returns true if scene changed. +void start_player_walk_to_point(int x, int y); // x and y are not corrected to lie within navmesh // game-specific constants #define SCENE_LOBBY 1 diff --git a/game.cpp b/game.cpp index 076b2c3..e8c72ca 100644 --- a/game.cpp +++ b/game.cpp @@ -1,3 +1,5 @@ +#include +#include #include "engine.h" #define BGWIDTH 1280 @@ -15,6 +17,7 @@ extern struct navmesh navmesh_lobby; #define OBJID_DOOR_TO_LOBBY_FROM_MANAGERS_OFFICE 3 // #define OBJID_PLAYER 4 // #define SCRIPTID_PLAYER_WALK 5 +#define OBJID_PLAYER_WALK_TO_DOOR_SCRIPT 6 void scene_setup(int scene) { switch(scene) { @@ -32,12 +35,29 @@ void scene_setup(int scene) { } } +static void transition_scene_on_walk_finish(struct script *scr, int wakeupMode, int arg1, int arg2, int arg3, int arg4) { + scr->id = 0; + printf("walk finish transition %d %d\n", wakeupMode, scr->vars[0]); + if(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT) + transition_scene(scr->vars[0]); + else + assert(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED); +} + void onclick(int curscene, int objid) { switch(curscene) { case SCENE_LOBBY: switch(objid) { case OBJID_DOOR_TO_MANAGERS_OFFICE_FROM_LOBBY: - transition_scene(SCENE_MANAGERS_OFFICE); + { + start_player_walk_to_point(312, 441); + + struct script *scr = scene_add_script(OBJID_PLAYER_WALK_TO_DOOR_SCRIPT, true); + scr->wakeupMode = SCRIPT_WAKEUP_OTHER_SCRIPT; + scr->wakeupArg1 = OBJID_PLAYER_WALK_SCRIPT; + scr->wakeupFn = transition_scene_on_walk_finish; + scr->vars[0] = SCENE_MANAGERS_OFFICE; + } return; } break; diff --git a/pinetab2_framework.cpp b/pinetab2_framework.cpp index ce369e8..63861bf 100644 --- a/pinetab2_framework.cpp +++ b/pinetab2_framework.cpp @@ -202,12 +202,13 @@ struct object *find_object_by_id(int id) { return nullptr; } struct script *scene_add_script(int id, bool interrupt_existing) { - if(interrupt_existing && id != OBJID_PLAYER_WALK_SCRIPT) error(1, 0, "scene_add_script: interrupt not implemented"); for(int i = 0; i < MAX_GAME_SCRIPTS; i++) { if(scripts[i].id == id && interrupt_existing) { // interrupt existing script? // player walk script is safe to trivially cancel, at least scripts[i].id = 0; + if(deliver_script_wakeup(SCRIPT_WAKEUP_OTHER_SCRIPT, id, SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED, 0, 0, 0, 0)) + return NULL; // scene changed. this probably crashes the caller. should be tested or we need a better way to cancel all running operations on scene change. } if(scripts[i].id == 0) { memset(&scripts[i], 0, sizeof(scripts[i])); @@ -294,38 +295,49 @@ static int find_navmesh_tri(int x, int y, int tolerance) { } static void update_player_walk_script_on_frame(struct script *scr, int wakeupMode, int, int, int, int); + +static uint32_t player_walk_generation = 0; +void start_player_walk_to_point(int targetX, int targetY) { + player_walk_generation++; + + struct object *player = find_object_by_id(OBJID_PLAYER); + if(!player) error(1, 0, "start_player_walk_to_point: no player object"); + + int destTri = find_navmesh_tri(targetX, targetY, 999999); + if(destTri == -1) error(1, 0, "start_player_walk_to_point: no navmesh"); + + struct script_player_walk *player_walk_script = (struct script_player_walk *)scene_add_script(OBJID_PLAYER_WALK_SCRIPT, true); + player_walk_script->targetX = targetX; + player_walk_script->targetY = targetY; + player_walk_script->targetNavmeshTri = destTri; + player_walk_script->wakeupMode = SCRIPT_WAKEUP_VIDEO_FRAME; + player_walk_script->wakeupFn = update_player_walk_script_on_frame; + + int sourceTri = find_navmesh_tri(player->x+player->width*2, player->y+player->height, 1000000); + player_walk_script->currentNavmeshTri = sourceTri; + printf("set course from triangle %d to %d\n", sourceTri, destTri); +} + void handle_tap(int x, int y) { uint32_t gencount = scene_generation_count; + uint32_t walkgen = player_walk_generation; for(int i = 0; i < MAX_GAME_OBJECTS; i++) { if(objects[i].id && (unsigned int)(x - objects[i].x) < objects[i].width && (unsigned int)(y - objects[i].y) < objects[i].height) { - if(gencount != scene_generation_count) return; // early exit if scene changed onclick(curscene, objects[i].id); + if(gencount != scene_generation_count) return; // early exit if scene changed, and don't walk the player either } } - struct script_player_walk *player_walk_script = (struct script_player_walk *)scene_add_script(OBJID_PLAYER_WALK_SCRIPT, true); - - struct object *player = find_object_by_id(OBJID_PLAYER); - if(player) { - int destTri = find_navmesh_tri(x, y, 50); - if(destTri != -1) { - int targetX = x, targetY = y; - closest_point_inside_tri(&targetX, &targetY, &cur_navmesh->tris[destTri]); - - player_walk_script->targetX = targetX; - player_walk_script->targetY = targetY; - player_walk_script->targetNavmeshTri = destTri; - player_walk_script->wakeupMode = SCRIPT_WAKEUP_VIDEO_FRAME; - player_walk_script->wakeupFn = update_player_walk_script_on_frame; - - int sourceTri = find_navmesh_tri(player->x+player->width*2, player->y+player->height, 1000000); - player_walk_script->currentNavmeshTri = sourceTri; - printf("set course from triangle %d to %d\n", sourceTri, destTri); - } else { - player_walk_script->id = 0; + if(walkgen == player_walk_generation) { // no other handler moved the player + struct object *player = find_object_by_id(OBJID_PLAYER); + if(player) { + int destTri = find_navmesh_tri(x, y, 50); + if(destTri != -1) { + int targetX = x, targetY = y; + closest_point_inside_tri(&targetX, &targetY, &cur_navmesh->tris[destTri]); + start_player_walk_to_point(targetX, targetY); + } } - } else { - player_walk_script->id = 0; } } @@ -336,11 +348,13 @@ static int move_towards(int cur, int target, int maxrate) { } static void update_player_walk_script_on_frame(struct script *scr, int wakeupMode, int, int, int, int) { + int script_id = scr->id; struct script_player_walk *player_walk_script = (struct script_player_walk *)scr; if(!player_walk_script) return; struct object *player = find_object_by_id(OBJID_PLAYER); if(!player) { player_walk_script->id = 0; + deliver_script_wakeup(SCRIPT_WAKEUP_OTHER_SCRIPT, script_id, SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED, 0, 0, 0, 0); return; } @@ -349,6 +363,7 @@ static void update_player_walk_script_on_frame(struct script *scr, int wakeupMod player->x = player_walk_script->targetX - player->width/2; player->y = player_walk_script->targetY - player->height; player_walk_script->id = 0; + deliver_script_wakeup(SCRIPT_WAKEUP_OTHER_SCRIPT, script_id, SCRIPT_WAKEUP_OTHER_SCRIPT, 0, 0, 0, 0); need_rerender = true; } else { int x = player->x+player->width/2; @@ -372,6 +387,7 @@ static void update_player_walk_script_on_frame(struct script *scr, int wakeupMod } // shouldn't happen player_walk_script->id = 0; + deliver_script_wakeup(SCRIPT_WAKEUP_OTHER_SCRIPT, script_id, SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED, 0, 0, 0, 0); return; center_found:; } @@ -379,6 +395,7 @@ static void update_player_walk_script_on_frame(struct script *scr, int wakeupMod printf("in triangle %d, next triangle %d\n", nmtri, nextTri); if(x == player_walk_script->targetX && y == player_walk_script->targetY) { player_walk_script->id = 0; + deliver_script_wakeup(SCRIPT_WAKEUP_OTHER_SCRIPT, script_id, SCRIPT_WAKEUP_OTHER_SCRIPT, 0, 0, 0, 0); } else { player_walk_script->currentNavmeshTri = nextTri; // even if -1 }