Manager's office safe scene stacks on top of manager's office scene, instead of replacing it.
parent
bbac8ccc44
commit
6cf3dbd607
33
engine.h
33
engine.h
|
@ -1,26 +1,39 @@
|
||||||
#define MAX_GAME_OBJECTS 100
|
#define MAX_OBJECTS_PER_SCENE 30
|
||||||
extern struct object {
|
#define MAX_SCRIPTS_PER_SCENE 30
|
||||||
|
#define MAX_STACKED_SCENES 5
|
||||||
|
|
||||||
|
struct object {
|
||||||
int id; // 0 if slot unused
|
int id; // 0 if slot unused
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
const char *pixels;
|
const char *pixels;
|
||||||
} objects[MAX_GAME_OBJECTS];
|
};
|
||||||
|
|
||||||
#define MAX_GAME_SCRIPTS 100
|
|
||||||
typedef void (*script_wake_fn)(struct script *scr, int wakeupMode, int arg1, int arg2, int arg3, int arg4);
|
typedef void (*script_wake_fn)(struct script *scr, int wakeupMode, int arg1, int arg2, int arg3, int arg4);
|
||||||
#define SCRIPT_HEADER \
|
#define SCRIPT_HEADER \
|
||||||
int id /* 0 if slot unused */; \
|
int id /* 0 if slot unused */; \
|
||||||
int wakeupMode; \
|
int wakeupMode; \
|
||||||
script_wake_fn wakeupFn; \
|
script_wake_fn wakeupFn; \
|
||||||
int wakeupArg1; /* for SCRIPT_WAKEUP_OTHER_SCRIPT */
|
int wakeupArg1; /* for SCRIPT_WAKEUP_OTHER_SCRIPT */
|
||||||
extern struct script {
|
struct script {
|
||||||
SCRIPT_HEADER
|
SCRIPT_HEADER
|
||||||
|
|
||||||
int vars[100];
|
int vars[100];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*scene_render_fn)(struct scene *s);
|
||||||
|
void standard_scene_render(struct scene *s);
|
||||||
|
extern struct scene {
|
||||||
|
int id;
|
||||||
|
struct object objects[MAX_OBJECTS_PER_SCENE];
|
||||||
|
struct script scripts[MAX_SCRIPTS_PER_SCENE];
|
||||||
|
scene_render_fn render_fn;
|
||||||
|
struct navmesh *navmesh; // defaults to non-null pointer to empty navmesh
|
||||||
|
} scenes[MAX_STACKED_SCENES];
|
||||||
|
extern int scene_depth; // number of stacked scenes
|
||||||
|
#define top_scene scenes[scene_depth-1]
|
||||||
|
|
||||||
} scripts[MAX_GAME_SCRIPTS];
|
|
||||||
|
|
||||||
struct script_player_walk {
|
struct script_player_walk {
|
||||||
SCRIPT_HEADER
|
SCRIPT_HEADER
|
||||||
|
@ -36,10 +49,12 @@ struct script_player_walk {
|
||||||
|
|
||||||
|
|
||||||
// engine
|
// engine
|
||||||
void transition_scene(int scene); // Immediately stops all event processing for current scene, which is unloaded before this function returns
|
// Scene changes - even push - may discard remaining events for the scene.
|
||||||
|
void push_scene(int scene);
|
||||||
|
void replace_scene(int scene);
|
||||||
|
void pop_scene();
|
||||||
void scene_add_clickrect(int id, int x, int y, int width, int height);
|
void scene_add_clickrect(int id, int x, int y, int width, int height);
|
||||||
void scene_add_object(int id, int x, int y, int width, int height, const char *pixels);
|
void scene_add_object(int id, int x, int y, int width, int height, const char *pixels);
|
||||||
void scene_set_navmesh(struct navmesh *navmesh);
|
|
||||||
struct object *find_object_by_id(int id);
|
struct object *find_object_by_id(int id);
|
||||||
struct script *scene_add_script(int id, bool interrupt_existing);
|
struct script *scene_add_script(int id, bool interrupt_existing);
|
||||||
struct script *scene_get_script(int id);
|
struct script *scene_get_script(int id);
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
At first we had a single scene at a time. This can't support some features we want.
|
||||||
|
|
||||||
|
We can have a stack of scenes.
|
||||||
|
A scene is often a room but it can also be a modal dialog.
|
||||||
|
When moving from one room to another it replaces the topmost scene but modal interactions push onto the stack.
|
||||||
|
Example: Manager's office -> Access the safe -> Textbox -> Pause/quit menu.
|
||||||
|
|
||||||
|
Hypothetically we could also have something like a dream world which keeps the outside-dream state on the stack.
|
||||||
|
|
||||||
|
Scenes come in different types. A room is a typical scene. But we have minigames like the safe, textboxes and menus.
|
||||||
|
All rooms should inherit some common behaviour like moving the player character.
|
||||||
|
All menus should inherit menu behaviour. Neither applies to the safe cracking.
|
||||||
|
|
||||||
|
Things from all scenes are rendered in scene stacking order.
|
20
game.cpp
20
game.cpp
|
@ -30,6 +30,7 @@ extern struct navmesh navmesh_basement;
|
||||||
#define OBJID_CLOSE_MODAL 12
|
#define OBJID_CLOSE_MODAL 12
|
||||||
|
|
||||||
void scene_setup(int scene, int fromscene) {
|
void scene_setup(int scene, int fromscene) {
|
||||||
|
top_scene.render_fn = standard_scene_render;
|
||||||
switch(scene) {
|
switch(scene) {
|
||||||
case SCENE_LOBBY:
|
case SCENE_LOBBY:
|
||||||
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_lobby_raw_start);
|
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_lobby_raw_start);
|
||||||
|
@ -42,7 +43,7 @@ void scene_setup(int scene, int fromscene) {
|
||||||
scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
|
scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
scene_set_navmesh(&navmesh_lobby);
|
top_scene.navmesh = &navmesh_lobby;
|
||||||
break;
|
break;
|
||||||
case SCENE_MANAGERS_OFFICE:
|
case SCENE_MANAGERS_OFFICE:
|
||||||
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_managers_office_raw_start);
|
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_managers_office_raw_start);
|
||||||
|
@ -63,10 +64,10 @@ void scene_setup(int scene, int fromscene) {
|
||||||
scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
|
scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
scene_set_navmesh(&navmesh_managers_office);
|
top_scene.navmesh = &navmesh_managers_office;
|
||||||
break;
|
break;
|
||||||
case SCENE_MANAGERS_OFFICE_SAFE:
|
case SCENE_MANAGERS_OFFICE_SAFE:
|
||||||
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_managers_office_safe_raw_start);
|
scene_add_object(OBJID_BACKGROUND, 248, 0, 787, BGHEIGHT, _binary_sprite_managers_office_safe_raw_start);
|
||||||
scene_add_object(OBJID_CLOSE_MODAL, 0, 0, 248, 800, nullptr);
|
scene_add_object(OBJID_CLOSE_MODAL, 0, 0, 248, 800, nullptr);
|
||||||
scene_add_object(OBJID_CLOSE_MODAL, 1035, 0, 1280-1035, 800, nullptr);
|
scene_add_object(OBJID_CLOSE_MODAL, 1035, 0, 1280-1035, 800, nullptr);
|
||||||
break;
|
break;
|
||||||
|
@ -81,15 +82,20 @@ void scene_setup(int scene, int fromscene) {
|
||||||
scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
|
scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
scene_set_navmesh(&navmesh_basement);
|
top_scene.navmesh = &navmesh_basement;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void transition_scene_on_walk_finish(struct script *scr, int wakeupMode, int arg1, int arg2, int arg3, int arg4) {
|
static void transition_scene_on_walk_finish(struct script *scr, int wakeupMode, int arg1, int arg2, int arg3, int arg4) {
|
||||||
scr->id = 0;
|
scr->id = 0;
|
||||||
if(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT)
|
if(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT) {
|
||||||
transition_scene(scr->vars[0]);
|
if(scr->vars[0] == SCENE_MANAGERS_OFFICE_SAFE) {
|
||||||
|
push_scene(scr->vars[0]);
|
||||||
|
} else {
|
||||||
|
replace_scene(scr->vars[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
assert(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED);
|
assert(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +142,7 @@ void onclick(int curscene, int objid) {
|
||||||
case SCENE_MANAGERS_OFFICE_SAFE:
|
case SCENE_MANAGERS_OFFICE_SAFE:
|
||||||
switch(objid) {
|
switch(objid) {
|
||||||
case OBJID_CLOSE_MODAL:
|
case OBJID_CLOSE_MODAL:
|
||||||
transition_scene(SCENE_MANAGERS_OFFICE);
|
pop_scene();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -26,9 +26,8 @@
|
||||||
static int touchscreen_fd;
|
static int touchscreen_fd;
|
||||||
|
|
||||||
static struct navmesh null_navmesh = {0};
|
static struct navmesh null_navmesh = {0};
|
||||||
struct navmesh *cur_navmesh = &null_navmesh;
|
struct scene scenes[MAX_STACKED_SCENES];
|
||||||
struct object objects[MAX_GAME_OBJECTS];
|
int scene_depth = 0;
|
||||||
struct script scripts[MAX_GAME_SCRIPTS];
|
|
||||||
bool need_rerender = false;
|
bool need_rerender = false;
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,26 +161,32 @@ static void blit(int x, int y, const struct sprite *spr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void scene_set_navmesh(struct navmesh *navmesh) {
|
static void scene_clear_and_setup(int scene, int fromscene) {
|
||||||
cur_navmesh = navmesh;
|
memset(&top_scene, 0, sizeof(top_scene));
|
||||||
}
|
top_scene.id = scene;
|
||||||
|
top_scene.navmesh = &null_navmesh;
|
||||||
|
|
||||||
static uint32_t scene_generation_count = 0;
|
scene_setup(scene, fromscene);
|
||||||
static int curscene = -1;
|
}
|
||||||
void transition_scene(int scene) {
|
void push_scene(int scene) {
|
||||||
int prevscene = curscene;
|
scene_depth++;
|
||||||
scene_generation_count++;
|
if(scene_depth > MAX_STACKED_SCENES) error(1, 0, "maximum scene depth exceeded");
|
||||||
curscene = scene;
|
scene_clear_and_setup(scene, -1);
|
||||||
memset(objects, 0, sizeof objects);
|
}
|
||||||
memset(scripts, 0, sizeof scripts); // script state is local to scene, so no cancel notification
|
void replace_scene(int scene) {
|
||||||
cur_navmesh = &null_navmesh;
|
scene_clear_and_setup(scene, top_scene.id);
|
||||||
scene_setup(scene, prevscene);
|
}
|
||||||
|
void pop_scene() {
|
||||||
|
if(scene_depth == 1) error(1, 0, "no scenes left");
|
||||||
|
scene_depth--;
|
||||||
|
// any cleanup?
|
||||||
}
|
}
|
||||||
|
|
||||||
void scene_add_object(int id, int x, int y, int width, int height, const char *pixels) {
|
void scene_add_object(int id, int x, int y, int width, int height, const char *pixels) {
|
||||||
if(id == 0) error(1, 0, "scene_add_object: id 0 is invalid");
|
if(id == 0) error(1, 0, "scene_add_object: id 0 is invalid");
|
||||||
|
|
||||||
for(int i = 0; i < MAX_GAME_OBJECTS; i++) {
|
struct object *objects = top_scene.objects;
|
||||||
|
for(int i = 0; i < MAX_OBJECTS_PER_SCENE; i++) {
|
||||||
if(objects[i].id == 0) {
|
if(objects[i].id == 0) {
|
||||||
objects[i].id = id;
|
objects[i].id = id;
|
||||||
objects[i].x = x;
|
objects[i].x = x;
|
||||||
|
@ -196,14 +201,16 @@ void scene_add_object(int id, int x, int y, int width, int height, const char *p
|
||||||
}
|
}
|
||||||
|
|
||||||
struct object *find_object_by_id(int id) {
|
struct object *find_object_by_id(int id) {
|
||||||
for(int i = 0; i < MAX_GAME_OBJECTS; i++) {
|
struct object *objects = top_scene.objects;
|
||||||
|
for(int i = 0; i < MAX_OBJECTS_PER_SCENE; i++) {
|
||||||
if(objects[i].id == id)
|
if(objects[i].id == id)
|
||||||
return &objects[i];
|
return &objects[i];
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
struct script *scene_add_script(int id, bool interrupt_existing) {
|
struct script *scene_add_script(int id, bool interrupt_existing) {
|
||||||
for(int i = 0; i < MAX_GAME_SCRIPTS; i++) {
|
struct script *scripts = top_scene.scripts;
|
||||||
|
for(int i = 0; i < MAX_SCRIPTS_PER_SCENE; i++) {
|
||||||
if(scripts[i].id == id && interrupt_existing) {
|
if(scripts[i].id == id && interrupt_existing) {
|
||||||
// interrupt existing script?
|
// interrupt existing script?
|
||||||
// player walk script is safe to trivially cancel, at least
|
// player walk script is safe to trivially cancel, at least
|
||||||
|
@ -310,6 +317,7 @@ static double is_point_in_tri(int x, int y, const struct navmesh_tri *tri) {
|
||||||
static int find_navmesh_tri(int x, int y, int tolerance) {
|
static int find_navmesh_tri(int x, int y, int tolerance) {
|
||||||
int best_tri = -1;
|
int best_tri = -1;
|
||||||
double best_dist = tolerance;
|
double best_dist = tolerance;
|
||||||
|
struct navmesh *cur_navmesh = top_scene.navmesh;
|
||||||
for(int i = 0; i < cur_navmesh->num_tris; i++) {
|
for(int i = 0; i < cur_navmesh->num_tris; i++) {
|
||||||
double dist = is_point_in_tri(x, y, &cur_navmesh->tris[i]);
|
double dist = is_point_in_tri(x, y, &cur_navmesh->tris[i]);
|
||||||
if(dist == 0)
|
if(dist == 0)
|
||||||
|
@ -347,12 +355,14 @@ void start_player_walk_to_point(int targetX, int targetY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_tap(int x, int y) {
|
void handle_tap(int x, int y) {
|
||||||
uint32_t gencount = scene_generation_count;
|
|
||||||
uint32_t walkgen = player_walk_generation;
|
uint32_t walkgen = player_walk_generation;
|
||||||
for(int i = 0; i < MAX_GAME_OBJECTS; i++) {
|
int sceneid = top_scene.id;
|
||||||
|
struct navmesh *cur_navmesh = top_scene.navmesh;
|
||||||
|
struct object *objects = top_scene.objects;
|
||||||
|
for(int i = 0; i < MAX_OBJECTS_PER_SCENE; i++) {
|
||||||
if(objects[i].id && (unsigned int)(x - objects[i].x) < objects[i].width && (unsigned int)(y - objects[i].y) < objects[i].height) {
|
if(objects[i].id && (unsigned int)(x - objects[i].x) < objects[i].width && (unsigned int)(y - objects[i].y) < objects[i].height) {
|
||||||
onclick(curscene, objects[i].id);
|
onclick(sceneid, objects[i].id);
|
||||||
if(gencount != scene_generation_count) return; // early exit if scene changed, and don't walk the player either
|
if(top_scene.id != sceneid) return; // early exit if scene changed, and don't walk the player either
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,6 +395,7 @@ static void update_player_walk_script_on_frame(struct script *scr, int wakeupMod
|
||||||
deliver_script_wakeup(SCRIPT_WAKEUP_OTHER_SCRIPT, script_id, SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED, 0, 0, 0, 0);
|
deliver_script_wakeup(SCRIPT_WAKEUP_OTHER_SCRIPT, script_id, SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED, 0, 0, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
struct navmesh *cur_navmesh = top_scene.navmesh;
|
||||||
|
|
||||||
if(player_walk_script->currentNavmeshTri == -1) {
|
if(player_walk_script->currentNavmeshTri == -1) {
|
||||||
// something weird happened. just teleport player and end movement.
|
// something weird happened. just teleport player and end movement.
|
||||||
|
@ -438,16 +449,26 @@ static void update_player_walk_script_on_frame(struct script *scr, int wakeupMod
|
||||||
}
|
}
|
||||||
|
|
||||||
bool deliver_script_wakeup(int wakeupMode, int wakeupArg1, int wakeupType, int arg1, int arg2, int arg3, int arg4) {
|
bool deliver_script_wakeup(int wakeupMode, int wakeupArg1, int wakeupType, int arg1, int arg2, int arg3, int arg4) {
|
||||||
uint32_t gen = scene_generation_count;
|
int scene = top_scene.id;
|
||||||
for(int i = 0; i < MAX_GAME_SCRIPTS; i++) {
|
struct script *scripts = top_scene.scripts;
|
||||||
|
for(int i = 0; i < MAX_SCRIPTS_PER_SCENE; i++) {
|
||||||
if(scripts[i].id && scripts[i].wakeupMode == wakeupMode && scripts[i].wakeupArg1 == wakeupArg1) {
|
if(scripts[i].id && scripts[i].wakeupMode == wakeupMode && scripts[i].wakeupArg1 == wakeupArg1) {
|
||||||
scripts[i].wakeupFn(&scripts[i], wakeupType, arg1, arg2, arg3, arg4);
|
scripts[i].wakeupFn(&scripts[i], wakeupType, arg1, arg2, arg3, arg4);
|
||||||
if(scene_generation_count != gen) return true; // scene changed - abort early
|
if(scene_depth == 0 || top_scene.id != scene) return true; // scene changed - abort early
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void standard_scene_render(scene *s) {
|
||||||
|
struct object *objects = s->objects;
|
||||||
|
for(int i = 0; i < MAX_OBJECTS_PER_SCENE; i++) {
|
||||||
|
if(objects[i].id != 0 && objects[i].pixels) {
|
||||||
|
struct sprite spr = {.width = objects[i].width, .height = objects[i].height, .pixels = (uint32_t*)objects[i].pixels};
|
||||||
|
blit(objects[i].x, objects[i].y, &spr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
@ -520,7 +541,7 @@ int main(int argc, char **argv) {
|
||||||
if(timerfd_settime(timerfd, 0, &tspec, NULL) < 0) error(1, errno, "timerfd_settime");
|
if(timerfd_settime(timerfd, 0, &tspec, NULL) < 0) error(1, errno, "timerfd_settime");
|
||||||
}
|
}
|
||||||
|
|
||||||
transition_scene(SCENE_LOBBY);
|
push_scene(SCENE_LOBBY);
|
||||||
|
|
||||||
int cur_buffer = 0;
|
int cur_buffer = 0;
|
||||||
|
|
||||||
|
@ -588,11 +609,8 @@ int main(int argc, char **argv) {
|
||||||
cur_buffer = 1-cur_buffer; // for testing, just keep writing into the same buffer
|
cur_buffer = 1-cur_buffer; // for testing, just keep writing into the same buffer
|
||||||
curfb = fbs[cur_buffer];
|
curfb = fbs[cur_buffer];
|
||||||
|
|
||||||
for(int i = 0; i < MAX_GAME_OBJECTS; i++) {
|
for(int s = 0; s < scene_depth; s++) {
|
||||||
if(objects[i].id != 0 && objects[i].pixels) {
|
scenes[s].render_fn(&scenes[s]);
|
||||||
struct sprite spr = {.width = objects[i].width, .height = objects[i].height, .pixels = (uint32_t*)objects[i].pixels};
|
|
||||||
blit(objects[i].x, objects[i].y, &spr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct drm_mode_crtc_page_flip flipcmd = {
|
struct drm_mode_crtc_page_flip flipcmd = {
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue