Added an inventory in a menu bar. Not all rooms are updated - only the lobby.

master
immibis 2025-02-19 03:09:45 +01:00
parent f5e5886827
commit 30c06a6efb
13 changed files with 163 additions and 58 deletions

View File

@ -1,6 +1,6 @@
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
OBJS = pinetab2_framework.o game.o textbox.o
OBJS = pinetab2_framework.o game.o textbox.o inventory.o
SPRITES = $(patsubst $(PROJECT_ROOT)sprites/%.xcf,%,$(wildcard $(PROJECT_ROOT)sprites/*.xcf))
NAVMESHES = $(patsubst $(PROJECT_ROOT)navmesh/%.tmx,%,$(wildcard $(PROJECT_ROOT)navmesh/*.tmx))

View File

@ -1,4 +1,5 @@
#define MAX_OBJECTS_PER_SCENE 30
#define INVENTORY_SIZE 12
#define MAX_OBJECTS_PER_SCENE (30+INVENTORY_SIZE)
#define MAX_SCRIPTS_PER_SCENE 30
#define MAX_STACKED_SCENES 5
@ -38,6 +39,9 @@ extern struct scene {
scene_animtimer_fn animtimer_fn; // default null
scene_handle_tap_fn handle_tap_fn; // default standard_handle_tap
struct navmesh *navmesh; // defaults to non-null pointer to empty navmesh
bool use_standard_inventory; // defaults to true
} scenes[MAX_STACKED_SCENES];
extern int scene_depth; // number of stacked scenes
#define top_scene scenes[scene_depth-1]
@ -55,7 +59,7 @@ struct script_player_walk {
#define SCRIPT_WAKEUP_OTHER_SCRIPT 2
#define SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED 3 // Delivered to script that registers for SCRIPT_WAKEUP_OTHER_SCRIPT if that script is interrupted instead of completing.
typedef void (*scene_setup_fn)(int scene, int fromscene);
typedef void (*scene_setup_fn)(scene *s, int scene, int fromscene);
// engine
// Scene changes - even push - may discard remaining events for the scene.
@ -64,8 +68,8 @@ void push_scene(int scene, scene_setup_fn setup_fn);
void replace_scene(int scene);
void pop_scene();
void scene_add_clickrect(int id, int x, int y, int width, int height);
struct object *scene_add_object(int id, int x, int y, int width, int height, const char *pixels);
void scene_load_predef(const struct level_predef_data *predef);
struct object *scene_add_object(scene *s, int id, int x, int y, int width, int height, const char *pixels);
void scene_load_predef(struct scene *sc, const struct level_predef_data *predef);
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);
@ -74,6 +78,10 @@ void start_player_walk_to_point(int x, int y); // x and y are not corrected to l
void push_scene_textbox(const char *text);
// standard inventory
void create_standard_inventory(scene *s);
void standard_inventory_onclick(struct object *obj);
// used during rendering
// pixel is 0xRRGGBB
extern uint32_t *curfb;
@ -90,5 +98,5 @@ void blit(int x, int y, int width, int height, uint32_t *pixels);
#define SCENE_TEXTBOX 5
// game.cpp
void scene_setup(int scene, int fromscene);
void scene_setup(scene *s, int scene, int fromscene);
void onclick(int curscene, struct object *obj);

View File

@ -2,8 +2,11 @@
#include <stdio.h>
#include "engine.h"
#include "objids.h"
#include "inventory.h"
#define BGWIDTH 1280
#define BGWIDTH 1066
#define BGHEIGHT 800
extern const char _binary_sprite_lobby_raw_start[];
@ -18,57 +21,59 @@ extern struct navmesh navmesh_managers_office;
extern struct navmesh navmesh_basement;
extern struct level_predef_data predef_basement, predef_lobby, predef_managers_office;
static void create_player(int x, int y) {
static void create_player(scene *me, int x, int y) {
const int WIDTH=51, HEIGHT=111;
scene_add_object(OBJID_PLAYER, x-WIDTH/2, y-HEIGHT, WIDTH, HEIGHT, _binary_sprite_stickman_raw_start);
scene_add_object(me, OBJID_PLAYER, x-WIDTH/2, y-HEIGHT, WIDTH, HEIGHT, _binary_sprite_stickman_raw_start);
}
void scene_setup(int scene, int fromscene) {
void scene_setup(scene *me, int scene, int fromscene) {
switch(scene) {
case SCENE_LOBBY:
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_lobby_raw_start);
scene_load_predef(&predef_lobby);
scene_add_object(me, OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_lobby_raw_start);
scene_load_predef(me, &predef_lobby);
switch(fromscene) {
case SCENE_MANAGERS_OFFICE:
create_player(256, 445);
create_player(me, 256, 445);
break;
default:
create_player(424, 675);
create_player(me, 424, 675);
break;
}
top_scene.navmesh = &navmesh_lobby;
push_scene_textbox("Welcome\n\nto\n\ntestgame");
if(fromscene == -1) {
push_scene_textbox("Welcome\n\nto\n\ntestgame");
}
break;
case SCENE_MANAGERS_OFFICE:
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_managers_office_raw_start);
scene_load_predef(&predef_managers_office);
scene_add_object(me, OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_managers_office_raw_start);
scene_load_predef(me, &predef_managers_office);
switch(fromscene) {
case SCENE_LOBBY:
create_player(804, 708);
create_player(me, 804, 708);
break;
case SCENE_BASEMENT:
create_player(408, 559);
create_player(me, 408, 559);
break;
default:
create_player(424, 675);
create_player(me, 424, 675);
break;
}
top_scene.navmesh = &navmesh_managers_office;
break;
case SCENE_MANAGERS_OFFICE_SAFE:
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);
scene_add_object(me, OBJID_BACKGROUND, 248, 0, 787, BGHEIGHT, _binary_sprite_managers_office_safe_raw_start);
scene_add_object(me, OBJID_CLOSE_MODAL, 0, 0, 248, 800, nullptr);
scene_add_object(me, OBJID_CLOSE_MODAL, 1035, 0, 1280-1035, 800, nullptr);
break;
case SCENE_BASEMENT:
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_basement_raw_start);
scene_load_predef(&predef_basement);
scene_add_object(me, OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_basement_raw_start);
scene_load_predef(me, &predef_basement);
switch(fromscene) {
case SCENE_MANAGERS_OFFICE:
create_player(435, 404);
create_player(me, 435, 404);
break;
default:
create_player(424, 675);
create_player(me, 424, 675);
break;
}
top_scene.navmesh = &navmesh_basement;
@ -102,7 +107,17 @@ static void start_player_walk_to_point_then_transition_scene(int x, int y, int s
static void do_popcorn_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) {
push_scene_textbox("You got popcorn.");
if(count_item_in_inventory(INVITEM_POPCORN) >= 3) {
push_scene_textbox(
"You already have\n"
"enough popcorn."
);
} else {
if(add_to_inventory(INVITEM_POPCORN) < 0)
push_scene_textbox("Inventory is full.");
else
push_scene_textbox("You got popcorn.");
}
}
else
assert(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED);
@ -122,12 +137,16 @@ void onclick(int curscene, struct object *obj) {
pop_scene();
return;
}
if(obj->id >= OBJID_INVENTORY_SLOTS && obj->id < OBJID_INVENTORY_SLOTS + INVENTORY_SIZE) {
standard_inventory_onclick(obj);
return;
}
switch(curscene) {
case SCENE_LOBBY:
switch(obj->id) {
case OBJID_DOOR_TO_MANAGERS_OFFICE_FROM_LOBBY:
start_player_walk_to_point_then_transition_scene(312, 441, SCENE_MANAGERS_OFFICE);
start_player_walk_to_point_then_transition_scene(256, 441, SCENE_MANAGERS_OFFICE);
return;
}
break;

59
inventory.cpp Normal file
View File

@ -0,0 +1,59 @@
#include "engine.h"
#include "objids.h"
#include "inventory.h"
#include <assert.h>
extern const char _binary_sprite_menubar_raw_start[];
extern const char _binary_sprite_item_popcorn_raw_start[];
static const char *const inventory_sprites[] = {
_binary_sprite_item_popcorn_raw_start
};
int inventory[INVENTORY_SIZE] = {
// no default items
};
static void create_inventory_object(scene *s, int index) {
int type = inventory[index];
if(type == INVITEM_BLANK) return;
int x = 1066 + 14 + (index & 1) * 100;
int y = 200 + (index >> 1) * 100;
struct object *obj = scene_add_object(s, OBJID_INVENTORY_SLOTS + index, x, y, 87, 87, inventory_sprites[type-1]);
}
void create_standard_inventory(struct scene *s) {
scene_add_object(s, OBJID_MENUBAR, 1066, 0, 234, 800, _binary_sprite_menubar_raw_start);
for(int i = 0; i < INVENTORY_SIZE; i++) {
create_inventory_object(s, i);
}
}
void standard_inventory_onclick(struct object *obj) {
int slot = obj->id - OBJID_INVENTORY_SLOTS;
assert(inventory[slot] != INVITEM_BLANK);
// does nothing for now
}
int count_item_in_inventory(int item) {
int count = 0;
for(int i = 0; i < INVENTORY_SIZE; i++)
if(inventory[i] == item)
count++;
return count;
}
int add_to_inventory(int item) {
// returns slot number or -1 if no space available
for(int i = 0; i < INVENTORY_SIZE; i++) {
if(!inventory[i]) {
inventory[i] = item;
// TODO: inventory should refresh when a scene is activated when a different scene is activated before.
// If not a fully custom rendering. Otherwise, popping a scene may revert inventory objects on screen to an earlier state.
create_inventory_object(&top_scene, i);
return i;
}
}
return -1;
}

10
inventory.h Normal file
View File

@ -0,0 +1,10 @@
enum invobject {
INVITEM_BLANK = 0, // must always be 0
INVITEM_POPCORN,
};
extern int inventory[INVENTORY_SIZE];
int count_item_in_inventory(int item);
int add_to_inventory(int item); // returns slot number or -1 if no space available

View File

@ -4,29 +4,29 @@
<image source="../build/default/lobby.png" width="1280" height="800"/>
</imagelayer>
<objectgroup id="3" name="clickable">
<object id="1" name="OBJID_DOOR_TO_MANAGERS_OFFICE_FROM_LOBBY" x="273" y="313" width="76" height="128"/>
<object id="1" name="OBJID_DOOR_TO_MANAGERS_OFFICE_FROM_LOBBY" x="227" y="312" width="64" height="128"/>
</objectgroup>
<objectgroup id="4" name="navmesh">
<object id="10" x="213" y="441">
<polygon points="0,0 135,85 191,-1"/>
<object id="10" x="178" y="440">
<polygon points="0,0 112,83 159,-1"/>
</object>
<object id="11" x="213" y="441">
<polygon points="0,0 -211,301 134,176 135,85"/>
<object id="11" x="178" y="441">
<polygon points="0,-1 -178,303 110,173 112,82"/>
</object>
<object id="13" x="2" y="742">
<polygon points="0,0 0,57 330,58 345,-125"/>
<object id="13" x="2" y="733">
<polygon points="-2,11 -2,67 275,67 286,-119"/>
</object>
<object id="14" x="2" y="799">
<polygon points="330,1 959,1 933,-174 345,-182"/>
<object id="14" x="2" y="740">
<polygon points="275,60 802,60 776,-117 286,-126"/>
</object>
<object id="16" x="860" y="440">
<polygon points="0,0 76,118 180,0"/>
<object id="16" x="717" y="440">
<polygon points="0,0 62,119 150,0"/>
</object>
<object id="17" x="937" y="558">
<polygon points="-1,0 -2,67 245,95 103,-118"/>
<object id="17" x="782" y="538">
<polygon points="-3,21 -4,85 187,82 85,-98"/>
</object>
<object id="18" x="939" y="559">
<polygon points="243,94 -4,66 22,241 341,241"/>
<object id="18" x="783" y="539">
<polygon points="186,81 -5,84 21,261 283,261"/>
</object>
</objectgroup>
</map>

View File

@ -11,3 +11,6 @@
#define OBJID_BASEMENT_LADDER 11
#define OBJID_CLOSE_MODAL 12
#define OBJID_POPCORN_BARREL 13
#define OBJID_MENUBAR 14
#define OBJID_INVENTORY_SLOTS 0x10000000 // + slot number

View File

@ -174,13 +174,19 @@ void fillrect(int x1, int y1, int width, int height, uint32_t pixel) {
}
static void scene_clear_and_setup(int scene, int fromscene, scene_setup_fn setup_fn) {
memset(&top_scene, 0, sizeof(top_scene));
top_scene.id = scene;
top_scene.navmesh = &null_navmesh;
top_scene.handle_tap_fn = standard_handle_tap;
top_scene.render_fn = standard_scene_render;
struct scene *me = &top_scene; // setup_fn might push additional scenes such as textboxes
memset(me, 0, sizeof(*me));
me->id = scene;
me->navmesh = &null_navmesh;
me->handle_tap_fn = standard_handle_tap;
me->render_fn = standard_scene_render;
me->use_standard_inventory = true;
setup_fn(scene, fromscene);
setup_fn(me, scene, fromscene);
if(me->use_standard_inventory) {
create_standard_inventory(me);
}
need_rerender = true;
}
@ -202,10 +208,10 @@ void pop_scene() {
need_rerender = true;
}
struct object *scene_add_object(int id, int x, int y, int width, int height, const char *pixels) {
struct object *scene_add_object(struct scene *sc, 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");
struct object *objects = top_scene.objects;
struct object *objects = sc->objects;
for(int i = 0; i < MAX_OBJECTS_PER_SCENE; i++) {
if(objects[i].id == 0) {
objects[i].id = id;
@ -220,9 +226,9 @@ struct object *scene_add_object(int id, int x, int y, int width, int height, con
error(1, 0, "too many game objects");
}
void scene_load_predef(const struct level_predef_data *predef) {
void scene_load_predef(struct scene *sc, const struct level_predef_data *predef) {
for(const struct level_clickregion *cr = predef->clickregions; cr->num_edges; cr++) {
struct object *obj = scene_add_object(cr->id, cr->x, cr->y, cr->width, cr->height, nullptr);
struct object *obj = scene_add_object(sc, cr->id, cr->x, cr->y, cr->width, cr->height, nullptr);
obj->clickregion = cr;
}
}

BIN
sprites/item_popcorn.xcf Normal file

Binary file not shown.

Binary file not shown.

BIN
sprites/menubar.xcf Normal file

Binary file not shown.

Binary file not shown.

View File

@ -52,10 +52,10 @@ static void textbox_handle_tap(struct scene *s, int x, int y) {
}
}
static void textbox_setup_fn(int scene, int fromscene) {
top_scene.render_fn = textbox_render_fn;
top_scene.animtimer_fn = textbox_animtimer_fn;
top_scene.handle_tap_fn = textbox_handle_tap;
static void textbox_setup_fn(scene *s, int scene, int fromscene) {
s->render_fn = textbox_render_fn;
s->animtimer_fn = textbox_animtimer_fn;
s->handle_tap_fn = textbox_handle_tap;
}