Add textbox on start, and popcorn barrels in the basement display a textbox.
parent
b7f31cdeaf
commit
f5e5886827
2
Makefile
2
Makefile
|
@ -1,6 +1,6 @@
|
|||
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
OBJS = pinetab2_framework.o game.o
|
||||
OBJS = pinetab2_framework.o game.o textbox.o
|
||||
SPRITES = $(patsubst $(PROJECT_ROOT)sprites/%.xcf,%,$(wildcard $(PROJECT_ROOT)sprites/*.xcf))
|
||||
NAVMESHES = $(patsubst $(PROJECT_ROOT)navmesh/%.tmx,%,$(wildcard $(PROJECT_ROOT)navmesh/*.tmx))
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ def parse_object_polygon_points(object):
|
|||
basey = int(object.attrib["y"])
|
||||
if polygon_data is None:
|
||||
# It's a rectangle by default
|
||||
width = float(object.attrib["width"])
|
||||
height = float(object.attrib["height"])
|
||||
width = int(object.attrib["width"])
|
||||
height = int(object.attrib["height"])
|
||||
points = [(basex, basey), (basex + width, basey), (basex + width, basey + height), (basex, basey + height)]
|
||||
else:
|
||||
points = []
|
||||
|
@ -53,6 +53,15 @@ def get_edge_equation(a, b):
|
|||
R *= norm
|
||||
return P,Q,R
|
||||
|
||||
def get_bounding_box(points):
|
||||
(x1,y1),(x2,y2) = points[0],points[0]
|
||||
for x,y in points:
|
||||
if x<x1: x1=x
|
||||
if x>x2: x2=x
|
||||
if y<y1: y1=y
|
||||
if y>y2: y2=y
|
||||
return x1,y1,x2,y2
|
||||
|
||||
for object in tree.findall("objectgroup[@name='navmesh']/object"):
|
||||
points = parse_object_polygon_points(object)
|
||||
|
||||
|
@ -130,7 +139,9 @@ out.write("\t(const struct level_clickregion[]){\n") # clickregions start
|
|||
for object in tree.findall("objectgroup[@name='clickable']/object"):
|
||||
out.write("\t\t{\n");
|
||||
points = parse_object_polygon_points(object)
|
||||
x1,y1,x2,y2 = get_bounding_box(points)
|
||||
out.write("\t\t\t"+object.attrib["name"]+",\n")
|
||||
out.write(f"\t\t\t{x1},{y1},{x2-x1},{y2-y1},\n")
|
||||
out.write("\t\t\t" + str(len(points)) + ",\n")
|
||||
out.write("\t\t\t(const struct level_clickregion_edge[]){\n")
|
||||
for a,b in points_to_edges(points):
|
||||
|
|
|
@ -30,6 +30,7 @@ struct level_clickregion_edge {
|
|||
};
|
||||
struct level_clickregion {
|
||||
int id;
|
||||
int x, y, width, height;
|
||||
int num_edges;
|
||||
const struct level_clickregion_edge *edges;
|
||||
};
|
||||
|
|
24
engine.h
24
engine.h
|
@ -2,6 +2,8 @@
|
|||
#define MAX_SCRIPTS_PER_SCENE 30
|
||||
#define MAX_STACKED_SCENES 5
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct object {
|
||||
int id; // 0 if slot unused
|
||||
int x;
|
||||
|
@ -24,12 +26,17 @@ struct script {
|
|||
};
|
||||
|
||||
typedef void (*scene_render_fn)(struct scene *s);
|
||||
typedef void (*scene_animtimer_fn)(struct scene *s);
|
||||
typedef void (*scene_handle_tap_fn)(struct scene *s, int x, int y);
|
||||
void standard_scene_render(struct scene *s);
|
||||
void standard_handle_tap(struct scene *s, int x, int y);
|
||||
extern struct scene {
|
||||
int id;
|
||||
struct object objects[MAX_OBJECTS_PER_SCENE];
|
||||
struct script scripts[MAX_SCRIPTS_PER_SCENE];
|
||||
scene_render_fn render_fn;
|
||||
scene_render_fn render_fn; // default standard_scene_render
|
||||
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
|
||||
} scenes[MAX_STACKED_SCENES];
|
||||
extern int scene_depth; // number of stacked scenes
|
||||
|
@ -48,10 +55,12 @@ 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);
|
||||
|
||||
// engine
|
||||
// Scene changes - even push - may discard remaining events for the scene.
|
||||
void push_scene(int scene);
|
||||
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);
|
||||
|
@ -63,12 +72,23 @@ 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
|
||||
|
||||
void push_scene_textbox(const char *text);
|
||||
|
||||
// used during rendering
|
||||
// pixel is 0xRRGGBB
|
||||
extern uint32_t *curfb;
|
||||
extern bool need_rerender;
|
||||
void fillrect(int x1, int y1, int width, int height, uint32_t pixel);
|
||||
void blit(int x, int y, int width, int height, uint32_t *pixels);
|
||||
|
||||
|
||||
// game-specific constants
|
||||
#define SCENE_LOBBY 1
|
||||
#define SCENE_MANAGERS_OFFICE 2
|
||||
#define SCENE_MANAGERS_OFFICE_SAFE 3
|
||||
#define SCENE_BASEMENT 4
|
||||
#define SCENE_TEXTBOX 5
|
||||
|
||||
// game.cpp
|
||||
void scene_setup(int scene, int fromscene);
|
||||
void onclick(int curscene, int objid);
|
||||
void onclick(int curscene, struct object *obj);
|
||||
|
|
43
game.cpp
43
game.cpp
|
@ -24,20 +24,20 @@ static void create_player(int x, int y) {
|
|||
}
|
||||
|
||||
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);
|
||||
scene_load_predef(&predef_lobby);
|
||||
switch(fromscene) {
|
||||
case SCENE_MANAGERS_OFFICE:
|
||||
create_player(308, 445);
|
||||
create_player(256, 445);
|
||||
break;
|
||||
default:
|
||||
create_player(424, 675);
|
||||
break;
|
||||
}
|
||||
top_scene.navmesh = &navmesh_lobby;
|
||||
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);
|
||||
|
@ -99,17 +99,40 @@ static void start_player_walk_to_point_then_transition_scene(int x, int y, int s
|
|||
scr->vars[0] = scene;
|
||||
}
|
||||
|
||||
void onclick(int curscene, int objid) {
|
||||
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.");
|
||||
}
|
||||
else
|
||||
assert(wakeupMode == SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED);
|
||||
}
|
||||
|
||||
static void start_player_walk_to_point_then_do_popcorn(int x, int y) {
|
||||
start_player_walk_to_point(x, y);
|
||||
|
||||
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 = do_popcorn_on_walk_finish;
|
||||
}
|
||||
|
||||
void onclick(int curscene, struct object *obj) {
|
||||
if(obj->id == OBJID_CLOSE_MODAL) {
|
||||
pop_scene();
|
||||
return;
|
||||
}
|
||||
|
||||
switch(curscene) {
|
||||
case SCENE_LOBBY:
|
||||
switch(objid) {
|
||||
switch(obj->id) {
|
||||
case OBJID_DOOR_TO_MANAGERS_OFFICE_FROM_LOBBY:
|
||||
start_player_walk_to_point_then_transition_scene(312, 441, SCENE_MANAGERS_OFFICE);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SCENE_MANAGERS_OFFICE:
|
||||
switch(objid) {
|
||||
switch(obj->id) {
|
||||
case OBJID_DOOR_TO_LOBBY_FROM_MANAGERS_OFFICE:
|
||||
start_player_walk_to_point_then_transition_scene(815, 713, SCENE_LOBBY);
|
||||
return;
|
||||
|
@ -122,16 +145,12 @@ void onclick(int curscene, int objid) {
|
|||
}
|
||||
break;
|
||||
case SCENE_BASEMENT:
|
||||
switch(objid) {
|
||||
switch(obj->id) {
|
||||
case OBJID_BASEMENT_LADDER:
|
||||
start_player_walk_to_point_then_transition_scene(438, 402, SCENE_MANAGERS_OFFICE);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SCENE_MANAGERS_OFFICE_SAFE:
|
||||
switch(objid) {
|
||||
case OBJID_CLOSE_MODAL:
|
||||
pop_scene();
|
||||
case OBJID_POPCORN_BARREL:
|
||||
start_player_walk_to_point_then_do_popcorn(obj->x + obj->width/2, obj->y + obj->height);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.9" tiledversion="1.9.2" orientation="orthogonal" renderorder="right-down" width="40" height="25" tilewidth="32" tileheight="32" infinite="0" nextlayerid="5" nextobjectid="4">
|
||||
<map version="1.9" tiledversion="1.9.2" orientation="orthogonal" renderorder="right-down" width="40" height="25" tilewidth="32" tileheight="32" infinite="0" nextlayerid="5" nextobjectid="8">
|
||||
<imagelayer id="2" name="Image Layer 1">
|
||||
<image source="../build/default/basement.png" width="1280" height="800"/>
|
||||
</imagelayer>
|
||||
|
@ -13,5 +13,9 @@
|
|||
</objectgroup>
|
||||
<objectgroup id="4" name="clickable">
|
||||
<object id="3" name="OBJID_BASEMENT_LADDER" x="396" y="86" width="85" height="312"/>
|
||||
<object id="4" name="OBJID_POPCORN_BARREL" x="529" y="291" width="84" height="150"/>
|
||||
<object id="5" name="OBJID_POPCORN_BARREL" x="628" y="291" width="84" height="150"/>
|
||||
<object id="6" name="OBJID_POPCORN_BARREL" x="733" y="293" width="83" height="148"/>
|
||||
<object id="7" name="OBJID_POPCORN_BARREL" x="835" y="295" width="81" height="146"/>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
|
1
objids.h
1
objids.h
|
@ -10,3 +10,4 @@
|
|||
#define OBJID_BASEMENT_ELECTRICAL_BOX 10
|
||||
#define OBJID_BASEMENT_LADDER 11
|
||||
#define OBJID_CLOSE_MODAL 12
|
||||
#define OBJID_POPCORN_BARREL 13
|
||||
|
|
|
@ -40,7 +40,6 @@ static struct mtslot {
|
|||
} mtslots[MAX_MT_SLOTS] = {0};
|
||||
static struct mtslot *curslot = &mtslots[0]; // TODO: query the current value of ABS_MT_SLOT
|
||||
|
||||
static void handle_tap(int x, int y); // x and y in landscape orientation
|
||||
static void poll_touchscreen() {
|
||||
struct input_event buf[64];
|
||||
int nread;
|
||||
|
@ -97,7 +96,7 @@ retry_read:
|
|||
}
|
||||
}*/
|
||||
if(!s->lasttouch) {
|
||||
handle_tap(s->y, 799-s->x);
|
||||
top_scene.handle_tap_fn(&top_scene, s->y, 799-s->x);
|
||||
}
|
||||
}
|
||||
s->lasttouch = s->touch;
|
||||
|
@ -131,21 +130,16 @@ retry_read:
|
|||
|
||||
uint32_t current_animframe;
|
||||
|
||||
struct sprite {
|
||||
int width, height;
|
||||
uint32_t *pixels;
|
||||
};
|
||||
|
||||
static uint32_t *curfb; // used during render cycle
|
||||
static void blit(int x, int y, const struct sprite *spr) {
|
||||
uint32_t *inbase = spr->pixels;
|
||||
uint32_t *curfb; // used during render cycle
|
||||
void blit(int x, int y, int width, int height, uint32_t *pixels) {
|
||||
uint32_t *inbase = pixels;
|
||||
//printf("blit %dx%d at %d,%d\n", spr->width, spr->height, x, y);
|
||||
y = PLAYFIELD_HEIGHT - spr->height - y;
|
||||
if(y >= PLAYFIELD_HEIGHT || x >= PLAYFIELD_WIDTH || y <= -spr->height || x <= -spr->width) return;
|
||||
if(spr->height > PLAYFIELD_HEIGHT) error(1, 0, "sprite bigger than playfield not supported"); // would need to clip top and bottom simultaneously - not implemented for now
|
||||
y = PLAYFIELD_HEIGHT - height - y;
|
||||
if(y >= PLAYFIELD_HEIGHT || x >= PLAYFIELD_WIDTH || y <= -height || x <= -width) return;
|
||||
if(height > PLAYFIELD_HEIGHT) error(1, 0, "sprite bigger than playfield not supported"); // would need to clip top and bottom simultaneously - not implemented for now
|
||||
|
||||
uint32_t *outbase = curfb + x*PLAYFIELD_HEIGHT + y;
|
||||
for(int dx = 0; dx < spr->width; dx++, inbase += spr->height, outbase += PLAYFIELD_HEIGHT, x++) {
|
||||
for(int dx = 0; dx < width; dx++, inbase += height, outbase += PLAYFIELD_HEIGHT, x++) {
|
||||
if(x < 0) continue;
|
||||
if(x >= PLAYFIELD_WIDTH) break;
|
||||
|
||||
|
@ -153,34 +147,59 @@ static void blit(int x, int y, const struct sprite *spr) {
|
|||
// TODO: loop inversion would make sense
|
||||
int offset = -y;
|
||||
// must have at least one pixel to copy
|
||||
memcpy(outbase+offset, inbase+offset, (spr->height-offset)*4);
|
||||
memcpy(outbase+offset, inbase+offset, (height-offset)*4);
|
||||
} else {
|
||||
int ncopy = PLAYFIELD_HEIGHT-y;
|
||||
if(ncopy > spr->height) ncopy = spr->height;
|
||||
if(ncopy > height) ncopy = height;
|
||||
memcpy(outbase, inbase, ncopy*4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void scene_clear_and_setup(int scene, int fromscene) {
|
||||
void fillrect(int x1, int y1, int width, int height, uint32_t pixel) {
|
||||
int x2 = x1 + width;
|
||||
int y2 = y1 + height;
|
||||
if(x1 < 0) x1 = 0;
|
||||
if(y1 < 0) y1 = 0;
|
||||
if(x2 >= PLAYFIELD_WIDTH) x2 = PLAYFIELD_WIDTH;
|
||||
if(y2 >= PLAYFIELD_HEIGHT) y2 = PLAYFIELD_HEIGHT;
|
||||
y1 = PLAYFIELD_HEIGHT-1-y1;
|
||||
y2 = PLAYFIELD_HEIGHT-1-y2;
|
||||
for(int x = x1; x < x2; x++) {
|
||||
uint32_t *outbase = curfb + x*PLAYFIELD_HEIGHT + y2;
|
||||
for(int y = y2; y < y1; y++) {
|
||||
*outbase++ = 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;
|
||||
|
||||
scene_setup(scene, fromscene);
|
||||
setup_fn(scene, fromscene);
|
||||
|
||||
need_rerender = true;
|
||||
}
|
||||
void push_scene(int scene) {
|
||||
void push_scene(int scene, scene_setup_fn setup_fn) {
|
||||
scene_depth++;
|
||||
if(scene_depth > MAX_STACKED_SCENES) error(1, 0, "maximum scene depth exceeded");
|
||||
scene_clear_and_setup(scene, -1);
|
||||
scene_clear_and_setup(scene, -1, setup_fn);
|
||||
}
|
||||
void push_scene(int scene) {
|
||||
push_scene(scene, scene_setup);
|
||||
}
|
||||
void replace_scene(int scene) {
|
||||
scene_clear_and_setup(scene, top_scene.id);
|
||||
scene_clear_and_setup(scene, top_scene.id, scene_setup);
|
||||
}
|
||||
void pop_scene() {
|
||||
if(scene_depth == 1) error(1, 0, "no scenes left");
|
||||
scene_depth--;
|
||||
// any cleanup?
|
||||
need_rerender = true;
|
||||
}
|
||||
|
||||
struct object *scene_add_object(int id, int x, int y, int width, int height, const char *pixels) {
|
||||
|
@ -203,7 +222,7 @@ struct object *scene_add_object(int id, int x, int y, int width, int height, con
|
|||
|
||||
void scene_load_predef(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, 0, 0, 0, 0, nullptr);
|
||||
struct object *obj = scene_add_object(cr->id, cr->x, cr->y, cr->width, cr->height, nullptr);
|
||||
obj->clickregion = cr;
|
||||
}
|
||||
}
|
||||
|
@ -362,9 +381,9 @@ void start_player_walk_to_point(int targetX, int targetY) {
|
|||
printf("set course from triangle %d to %d\n", sourceTri, destTri);
|
||||
}
|
||||
|
||||
void handle_tap(int x, int y) {
|
||||
void standard_handle_tap(struct scene *scene, int x, int y) {
|
||||
uint32_t walkgen = player_walk_generation;
|
||||
int sceneid = top_scene.id;
|
||||
int sceneid = 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++) {
|
||||
|
@ -381,7 +400,7 @@ void handle_tap(int x, int y) {
|
|||
}
|
||||
|
||||
// clicked on the object
|
||||
onclick(sceneid, objects[i].id);
|
||||
onclick(sceneid, &objects[i]);
|
||||
if(top_scene.id != sceneid) return; // early exit if scene changed, and don't walk the player either
|
||||
|
||||
click_not_on_object:;
|
||||
|
@ -485,8 +504,7 @@ 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);
|
||||
blit(objects[i].x, objects[i].y, objects[i].width, objects[i].height, (uint32_t*)objects[i].pixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -558,7 +576,8 @@ int main(int argc, char **argv) {
|
|||
int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||
if(timerfd < 0) error(1, errno, "timerfd_create");
|
||||
{
|
||||
struct itimerspec tspec = {.it_interval = {.tv_nsec = 250000000}, .it_value = {.tv_nsec=250000000}};
|
||||
#define ANIMTIMER_INTERVAL 75000000
|
||||
struct itimerspec tspec = {.it_interval = {.tv_nsec = ANIMTIMER_INTERVAL}, .it_value = {.tv_nsec = ANIMTIMER_INTERVAL}};
|
||||
if(timerfd_settime(timerfd, 0, &tspec, NULL) < 0) error(1, errno, "timerfd_settime");
|
||||
}
|
||||
|
||||
|
@ -596,7 +615,10 @@ int main(int argc, char **argv) {
|
|||
if(read(timerfd, &dummy, sizeof dummy) < 0) error(1, errno, "read (timerfd)");
|
||||
current_animframe++;
|
||||
//printf("animation frame %u\n", (unsigned int)current_animframe);
|
||||
need_rerender = true;
|
||||
|
||||
if(top_scene.animtimer_fn) {
|
||||
top_scene.animtimer_fn(&top_scene);
|
||||
}
|
||||
}
|
||||
if(polls[2].revents & POLLIN) {
|
||||
char buf[256];
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,81 @@
|
|||
#include "engine.h"
|
||||
#include "objids.h"
|
||||
#include <string.h>
|
||||
|
||||
// TODO: should be scene variables, so multiple textboxes can be stacked
|
||||
static const char *textbox_text;
|
||||
static int tb_width_chars, tb_height_chars;
|
||||
static int visible_chars = 0;
|
||||
|
||||
extern const char _binary_sprite_font_raw_start[];
|
||||
|
||||
|
||||
|
||||
#define CHARSIZE 24
|
||||
#define HSPACE 2
|
||||
#define LINESPACE 8
|
||||
|
||||
static void textbox_render_fn(struct scene *s) {
|
||||
int boxheight = tb_height_chars*(CHARSIZE + LINESPACE) - LINESPACE;
|
||||
int boxwidth = tb_width_chars*(CHARSIZE + HSPACE) - HSPACE;
|
||||
int x = (1280 - tb_width_chars*CHARSIZE)/2;
|
||||
int y = (800 - tb_height_chars*CHARSIZE)/2;
|
||||
int xleft = x;
|
||||
|
||||
fillrect(x-CHARSIZE, y-CHARSIZE, boxwidth+2*CHARSIZE, boxheight+2*CHARSIZE, 0);
|
||||
|
||||
for(const char *str = textbox_text; *str; str++) {
|
||||
if(str == textbox_text + visible_chars)
|
||||
break;
|
||||
if(*str == '\n') {
|
||||
x = xleft;
|
||||
y += CHARSIZE + LINESPACE;
|
||||
} else {
|
||||
blit(x, y, CHARSIZE, CHARSIZE, (*str - 32)*(CHARSIZE*CHARSIZE) + (uint32_t*)_binary_sprite_font_raw_start);
|
||||
x += CHARSIZE + HSPACE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void textbox_animtimer_fn(struct scene *s) {
|
||||
if(textbox_text[visible_chars]) {
|
||||
visible_chars++;
|
||||
need_rerender = true;
|
||||
}
|
||||
}
|
||||
static void textbox_handle_tap(struct scene *s, int x, int y) {
|
||||
if(textbox_text[visible_chars]) {
|
||||
visible_chars = strlen(textbox_text);
|
||||
need_rerender = true;
|
||||
} else {
|
||||
pop_scene();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void push_scene_textbox(const char *text) {
|
||||
textbox_text = text;
|
||||
visible_chars = 0;
|
||||
|
||||
tb_width_chars = 0;
|
||||
tb_height_chars = 1;
|
||||
int cur_width = 0;
|
||||
for(const char *s = text; *s; s++) {
|
||||
if(*s == '\n') {
|
||||
tb_height_chars++;
|
||||
cur_width = 0;
|
||||
} else {
|
||||
cur_width++;
|
||||
if(cur_width > tb_width_chars)
|
||||
tb_width_chars = cur_width;
|
||||
}
|
||||
}
|
||||
|
||||
push_scene(SCENE_TEXTBOX, textbox_setup_fn);
|
||||
}
|
Loading…
Reference in New Issue