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
|
||||
extern struct object {
|
||||
#define MAX_OBJECTS_PER_SCENE 30
|
||||
#define MAX_SCRIPTS_PER_SCENE 30
|
||||
#define MAX_STACKED_SCENES 5
|
||||
|
||||
struct object {
|
||||
int id; // 0 if slot unused
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
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);
|
||||
#define SCRIPT_HEADER \
|
||||
int id /* 0 if slot unused */; \
|
||||
int wakeupMode; \
|
||||
script_wake_fn wakeupFn; \
|
||||
int wakeupArg1; /* for SCRIPT_WAKEUP_OTHER_SCRIPT */
|
||||
extern struct script {
|
||||
struct script {
|
||||
SCRIPT_HEADER
|
||||
|
||||
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 {
|
||||
SCRIPT_HEADER
|
||||
|
@ -36,10 +49,12 @@ struct script_player_walk {
|
|||
|
||||
|
||||
// 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_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 script *scene_add_script(int id, bool interrupt_existing);
|
||||
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
|
||||
|
||||
void scene_setup(int scene, int fromscene) {
|
||||
top_scene.render_fn = standard_scene_render;
|
||||
switch(scene) {
|
||||
case SCENE_LOBBY:
|
||||
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);
|
||||
break;
|
||||
}
|
||||
scene_set_navmesh(&navmesh_lobby);
|
||||
top_scene.navmesh = &navmesh_lobby;
|
||||
break;
|
||||
case SCENE_MANAGERS_OFFICE:
|
||||
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);
|
||||
break;
|
||||
}
|
||||
scene_set_navmesh(&navmesh_managers_office);
|
||||
top_scene.navmesh = &navmesh_managers_office;
|
||||
break;
|
||||
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, 1035, 0, 1280-1035, 800, nullptr);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
scene_set_navmesh(&navmesh_basement);
|
||||
top_scene.navmesh = &navmesh_basement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void transition_scene_on_walk_finish(struct script *scr, int wakeupMode, int arg1, int arg2, int arg3, int arg4) {
|
||||
scr->id = 0;
|
||||
if(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT)
|
||||
transition_scene(scr->vars[0]);
|
||||
if(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT) {
|
||||
if(scr->vars[0] == SCENE_MANAGERS_OFFICE_SAFE) {
|
||||
push_scene(scr->vars[0]);
|
||||
} else {
|
||||
replace_scene(scr->vars[0]);
|
||||
}
|
||||
}
|
||||
else
|
||||
assert(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED);
|
||||
}
|
||||
|
@ -136,7 +142,7 @@ void onclick(int curscene, int objid) {
|
|||
case SCENE_MANAGERS_OFFICE_SAFE:
|
||||
switch(objid) {
|
||||
case OBJID_CLOSE_MODAL:
|
||||
transition_scene(SCENE_MANAGERS_OFFICE);
|
||||
pop_scene();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -26,9 +26,8 @@
|
|||
static int touchscreen_fd;
|
||||
|
||||
static struct navmesh null_navmesh = {0};
|
||||
struct navmesh *cur_navmesh = &null_navmesh;
|
||||
struct object objects[MAX_GAME_OBJECTS];
|
||||
struct script scripts[MAX_GAME_SCRIPTS];
|
||||
struct scene scenes[MAX_STACKED_SCENES];
|
||||
int scene_depth = 0;
|
||||
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) {
|
||||
cur_navmesh = navmesh;
|
||||
}
|
||||
static void scene_clear_and_setup(int scene, int fromscene) {
|
||||
memset(&top_scene, 0, sizeof(top_scene));
|
||||
top_scene.id = scene;
|
||||
top_scene.navmesh = &null_navmesh;
|
||||
|
||||
static uint32_t scene_generation_count = 0;
|
||||
static int curscene = -1;
|
||||
void transition_scene(int scene) {
|
||||
int prevscene = curscene;
|
||||
scene_generation_count++;
|
||||
curscene = scene;
|
||||
memset(objects, 0, sizeof objects);
|
||||
memset(scripts, 0, sizeof scripts); // script state is local to scene, so no cancel notification
|
||||
cur_navmesh = &null_navmesh;
|
||||
scene_setup(scene, prevscene);
|
||||
scene_setup(scene, fromscene);
|
||||
}
|
||||
void push_scene(int scene) {
|
||||
scene_depth++;
|
||||
if(scene_depth > MAX_STACKED_SCENES) error(1, 0, "maximum scene depth exceeded");
|
||||
scene_clear_and_setup(scene, -1);
|
||||
}
|
||||
void replace_scene(int scene) {
|
||||
scene_clear_and_setup(scene, top_scene.id);
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
objects[i].id = id;
|
||||
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) {
|
||||
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)
|
||||
return &objects[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
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) {
|
||||
// interrupt existing script?
|
||||
// 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) {
|
||||
int best_tri = -1;
|
||||
double best_dist = tolerance;
|
||||
struct navmesh *cur_navmesh = top_scene.navmesh;
|
||||
for(int i = 0; i < cur_navmesh->num_tris; i++) {
|
||||
double dist = is_point_in_tri(x, y, &cur_navmesh->tris[i]);
|
||||
if(dist == 0)
|
||||
|
@ -347,12 +355,14 @@ void start_player_walk_to_point(int targetX, int targetY) {
|
|||
}
|
||||
|
||||
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++) {
|
||||
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) {
|
||||
onclick(curscene, objects[i].id);
|
||||
if(gencount != scene_generation_count) return; // early exit if scene changed, and don't walk the player either
|
||||
onclick(sceneid, objects[i].id);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
struct navmesh *cur_navmesh = top_scene.navmesh;
|
||||
|
||||
if(player_walk_script->currentNavmeshTri == -1) {
|
||||
// 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) {
|
||||
uint32_t gen = scene_generation_count;
|
||||
for(int i = 0; i < MAX_GAME_SCRIPTS; i++) {
|
||||
int scene = top_scene.id;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
|
@ -520,7 +541,7 @@ int main(int argc, char **argv) {
|
|||
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;
|
||||
|
||||
|
@ -588,11 +609,8 @@ int main(int argc, char **argv) {
|
|||
cur_buffer = 1-cur_buffer; // for testing, just keep writing into the same buffer
|
||||
curfb = fbs[cur_buffer];
|
||||
|
||||
for(int i = 0; i < MAX_GAME_OBJECTS; 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);
|
||||
}
|
||||
for(int s = 0; s < scene_depth; s++) {
|
||||
scenes[s].render_fn(&scenes[s]);
|
||||
}
|
||||
|
||||
struct drm_mode_crtc_page_flip flipcmd = {
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue