summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-13 00:01:11 +0800
committerKonAki <konaki@anokonaki.org>2026-10-13 00:01:11 +0800
commitc3e0f2d163d5dc0b2a9e19b72d85c1611ef95270 (patch)
treec568f94aa4bfe1226c6d81fd96a67dd080c4bdff /examples
parent3896db0ea50d1e91c29638d6db81a9b66b0c1ba8 (diff)
add script example: konscript driving stage and text box
Diffstat (limited to 'examples')
-rw-r--r--examples/script.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/examples/script.c b/examples/script.c
new file mode 100644
index 0000000..53ca718
--- /dev/null
+++ b/examples/script.c
@@ -0,0 +1,316 @@
+/*
+ * script: a konscript script driving the stage and the text box. the script is the list near the top, the rest
+ * is the program doing what the script asks for
+ *
+ * build: ./build.sh -d -m 2d,stage,textbox,script,fx,font obj/script
+ * run: obj/script
+ *
+ * space, enter or a click goes on, up and down or the mouse pick in the menu
+ */
+
+#include <stdio.h>
+
+#include "konprogram.h"
+#include "kon2d.h"
+#include "konstage.h"
+#include "kontextbox.h"
+#include "konfx.h"
+#include "konscript.h"
+
+#if !defined(KON_MODULE_2D) || !defined(KON_MODULE_STAGE) || !defined(KON_MODULE_TEXTBOX) || !defined(KON_MODULE_SCRIPT) || !defined(KON_MODULE_FX) || !defined(KON_MODULE_FONT)
+#error "build with: ./build.sh -d -m 2d,stage,textbox,script,fx,font obj/script"
+#endif
+
+#define VIEW_WIDTH 320
+#define VIEW_HEIGHT 180
+
+#define COL_BACKGROUND 0xFF1B1826u
+#define COL_PINK 0xFFF2A7C3u
+#define COL_PINK_DARK 0xFFB5738Fu
+#define COL_TEAL 0xFF8FD3C8u
+#define COL_TEAL_DARK 0xFF5A9A90u
+#define COL_SAND 0xFFF3DDA0u
+
+enum { ADA, BO, PEOPLE };
+enum { H_ENTER, H_LEAVE, H_FLASH }; /* our own commands, the script only knows their numbers */
+enum { SW_EXCUSED }; /* switches */
+enum { V_EXCUSES }; /* variables */
+
+/*** the script ***/
+
+static const kon_cmd_t script[] = {
+ /* 0 */ KON_HOST(H_ENTER, ADA, KON_SLOT_LEFT, 0, NULL),
+ /* 1 */ KON_SAY(ADA, "Morning."),
+ /* 2 */ KON_HOST(H_ENTER, BO, KON_SLOT_FAR_RIGHT, 0, NULL),
+ /* 3 */ KON_SAY(BO, "You're late. Again."),
+ /* 4 */ KON_MENU(3),
+ /* 5 */ KON_OPTION("Sorry.", 9, -1),
+ /* 6 */ KON_OPTION("There was traffic.", 12, -1),
+ /* 7 */ KON_OPTION("About the key...", 17, SW_EXCUSED),
+ /* 8 */ KON_END(),
+ /* 9 */ KON_SAY(BO, "Fine. Just don't make it a habit."),
+ /* 10 */ KON_VAR(V_EXCUSES, KON_SET, 0),
+ /* 11 */ KON_JUMP(20),
+ /* 12 */ KON_SAY(BO, "There is always traffic."),
+ /* 13 */ KON_SAY(ADA, "There was a lot of it today."),
+ /* 14 */ KON_VAR(V_EXCUSES, KON_ADD, 1),
+ /* 15 */ KON_SWITCH(SW_EXCUSED, 1),
+ /* 16 */ KON_JUMP(20),
+ /* 17 */ KON_SAY(BO, "What about it?"),
+ /* 18 */ KON_SAY(ADA, "Never mind."),
+ /* 19 */ KON_JUMP(20),
+ /* 20 */ KON_IFVAR(V_EXCUSES, KON_IS_LT, 2, 23),
+ /* 21 */ KON_SAY(BO, "Anyway. Again."),
+ /* 22 */ KON_JUMP(4),
+ /* 23 */ KON_SAY(BO, "That's the second excuse. I'm going."),
+ /* 24 */ KON_HOST(H_FLASH, 0, 0, 0, NULL),
+ /* 25 */ KON_HOST(H_LEAVE, BO, 0, 0, NULL),
+ /* 26 */ KON_HOST(H_LEAVE, ADA, 0, 0, NULL),
+ /* 27 */ KON_WAIT(800),
+ /* 28 */ KON_END()
+};
+
+/*** the program ***/
+
+typedef struct state {
+ kon_stage_t stage;
+ kon_stageSprite_t sprites[PEOPLE];
+ kon_textbox_t box, menu;
+ kon_screenfx_t fx;
+ kon_image *back, *block[PEOPLE];
+ int who[PEOPLE];
+ kon_script_t vm;
+ uint8_t switches[8];
+ int32_t vars[8];
+ int shown;
+} state_t;
+
+static kon_image *makeBlock(int width, int height, uint32_t fill, uint32_t edge)
+{
+ kon_image *image = kon_createFramebuffer(width, height);
+
+ if (!image) return NULL;
+ kon_clearFramebuffer(image, edge);
+ kon_fillRectangle(image, 3, 3, width - 6, height - 6, fill);
+ return image;
+}
+
+static kon_image *makeGradient(uint32_t top, uint32_t bottom)
+{
+ kon_image *image = kon_createFramebuffer(2, VIEW_HEIGHT);
+ int x, y;
+
+ if (!image) return NULL;
+ for (y = 0; y < VIEW_HEIGHT; y++) {
+ uint32_t c = 0xFF000000u;
+ int shift;
+
+ for (shift = 0; shift < 24; shift += 8) {
+ int a = (int)((top >> shift) & 0xFF), b = (int)((bottom >> shift) & 0xFF);
+
+ c |= (uint32_t)(a + (b - a) * y / (VIEW_HEIGHT - 1)) << shift;
+ }
+ for (x = 0; x < 2; x++) image->data[y * 2 + x] = c;
+ }
+ return image;
+}
+
+static int host(kon_script_t *vm, const kon_cmd_t *cmd, void *user)
+{
+ state_t *state = user;
+
+ (void)vm;
+ switch (cmd->a) {
+ case H_ENTER: {
+ int who = cmd->b, i = kon_stageAdd(&state->stage, state->block[who], NULL, KON_LAYER_BACK);
+
+ if (i < 0) break;
+ state->who[who] = i;
+ kon_stageMove(&state->stage, i, cmd->c == KON_SLOT_LEFT ? -30.0f : VIEW_WIDTH + 30.0f, VIEW_HEIGHT, 0.0f, KON_EASE_LINEAR);
+ kon_stageFade(&state->stage, i, 0.0f, 0.0f, KON_EASE_LINEAR);
+ kon_stageFade(&state->stage, i, 1.0f, 0.5f, KON_EASE_OUT);
+ kon_stagePlace(&state->stage, i, (kon_slot_t)cmd->c, 0.6f, KON_EASE_OUT);
+ break;
+ }
+ case H_LEAVE:
+ if (state->who[cmd->b] >= 0) kon_stageHide(&state->stage, state->who[cmd->b], 0.6f);
+ break;
+ case H_FLASH:
+ kon_screenfxFlash(&state->fx, 0xC0FFFFFFu, 0.4f);
+ break;
+ }
+ return 0;
+}
+
+static void restart(state_t *state)
+{
+ int i;
+
+ for (i = 0; i < PEOPLE; i++) {
+ if (state->who[i] >= 0) kon_stageRemove(&state->stage, state->who[i]);
+ state->who[i] = -1;
+ }
+ for (i = 0; i < 8; i++) state->switches[i] = 0, state->vars[i] = 0;
+ kon_scriptStart(&state->vm, 0);
+ state->shown = 0;
+}
+
+static void showMenu(state_t *state)
+{
+ const char *labels[KON_SCRIPT_MAX_OPTIONS];
+ int i, n = kon_scriptOptionCount(&state->vm);
+
+ if (n > KON_TEXTBOX_CHOICES) n = KON_TEXTBOX_CHOICES;
+ for (i = 0; i < n; i++) labels[i] = kon_scriptOption(&state->vm, i);
+ kon_textboxChoices(&state->menu, labels, n);
+ kon_textboxFitChoices(&state->menu, 170);
+ kon_textboxCenter(&state->menu, VIEW_WIDTH, 0, 1, 0);
+ kon_textboxMove(&state->menu, kon_tweenValue(&state->menu.x), (float)((int)kon_tweenValue(&state->box.y) - state->menu.height) / 2.0f, 0.0f, KON_EASE_LINEAR);
+}
+
+static void action(state_t *state)
+{
+ if (state->menu.state == KON_TEXTBOX_CHOOSING) {
+ kon_scriptChoose(&state->vm, kon_textboxConfirm(&state->menu));
+ state->shown = 0;
+ } else {
+ kon_textboxAdvance(&state->box);
+ }
+}
+
+static int event(kon_program_t *program, const kon_event_t *ev, void *user)
+{
+ state_t *state = user;
+
+ if (ev->type == KON_EVENT_KEY_DOWN) {
+ if (ev->key == KON_KEY_SPACE || ev->key == KON_KEY_ENTER) action(state);
+ else if (ev->key == KON_KEY_UP) kon_textboxSelect(&state->menu, -1);
+ else if (ev->key == KON_KEY_DOWN) kon_textboxSelect(&state->menu, 1);
+ else return 0;
+ } else if (ev->type == KON_EVENT_MOUSE_MOVE) {
+ int x, y;
+
+ if (kon_windowToGame(program->display, ev->mouseX, ev->mouseY, &x, &y)) kon_textboxHighlight(&state->menu, kon_textboxChoiceAt(&state->menu, x, y));
+ } else if (ev->type == KON_EVENT_MOUSE_DOWN && ev->mouseButton == KON_MOUSE_LEFT) {
+ int x, y;
+
+ if (state->menu.state == KON_TEXTBOX_CHOOSING) {
+ if (kon_windowToGame(program->display, ev->mouseX, ev->mouseY, &x, &y) && kon_textboxChoiceAt(&state->menu, x, y) >= 0) action(state);
+ } else {
+ action(state);
+ }
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+static void update(kon_program_t *program, double dt, void *user)
+{
+ state_t *state = user;
+
+ (void)program;
+ kon_stageUpdate(&state->stage, (float)dt);
+ kon_screenfxUpdate(&state->fx, (float)dt);
+ kon_textboxUpdate(&state->box, (float)dt);
+ kon_textboxUpdate(&state->menu, (float)dt);
+ kon_scriptUpdate(&state->vm, (float)dt);
+
+ switch (kon_scriptRun(&state->vm)) {
+ case KON_SCRIPT_SAY:
+ if (!state->shown) {
+ const kon_cmd_t *cmd = kon_scriptCurrent(&state->vm);
+
+ kon_textboxSay(&state->box, cmd->a == ADA ? "Ada" : "Bo", cmd->text);
+ state->shown = 1;
+ } else if (state->box.state == KON_TEXTBOX_EMPTY) {
+ kon_scriptResume(&state->vm);
+ state->shown = 0;
+ }
+ break;
+ case KON_SCRIPT_MENU:
+ if (!state->shown) {
+ showMenu(state);
+ state->shown = 1;
+ }
+ break;
+ case KON_SCRIPT_DONE:
+ case KON_SCRIPT_ERROR:
+ if (!kon_stageBusy(&state->stage)) restart(state);
+ break;
+ default:
+ break;
+ }
+}
+
+static void draw(kon_program_t *program, kon_framebuffer_t *game, void *user)
+{
+ state_t *state = user;
+
+ (void)program;
+ kon_clearFramebuffer(game, COL_BACKGROUND);
+ kon_stageDrawBackground(&state->stage, game);
+ kon_stageDraw(&state->stage, game, KON_LAYER_BACK);
+ kon_screenfxApply(&state->fx, game);
+ kon_textboxDraw(&state->box, game);
+ kon_stageDraw(&state->stage, game, KON_LAYER_FRONT);
+ kon_textboxDraw(&state->menu, game);
+}
+
+static int init(kon_program_t *program, void *user)
+{
+ state_t *state = user;
+
+ state->back = makeGradient(0xFF2E2848u, COL_BACKGROUND);
+ state->block[ADA] = makeBlock(64, 120, COL_PINK, COL_PINK_DARK);
+ state->block[BO] = makeBlock(64, 132, COL_TEAL, COL_TEAL_DARK);
+ if (!state->back || !state->block[ADA] || !state->block[BO]) return 0;
+
+ kon_stageInit(&state->stage, state->sprites, PEOPLE, VIEW_WIDTH, VIEW_HEIGHT);
+ kon_stageBackground(&state->stage, state->back, KON_FIT_STRETCH, 0.0f);
+ kon_screenfxInit(&state->fx);
+
+ kon_textboxInit(&state->box, program->font, 12, 118, VIEW_WIDTH - 24, 52);
+ state->box.panel.fill = 0xA026223Au;
+ state->box.plate.fill = 0xE0332E4Eu;
+ kon_textboxInit(&state->menu, program->font, 0, 0, 170, 40);
+ state->menu.choiceMode = KON_CHOICES_BUTTONS;
+ state->menu.choiceAlign = KON_ALIGN_CENTER;
+ state->menu.buttonSelected.fill = COL_SAND;
+
+ kon_scriptInit(&state->vm, script, (int)(sizeof script / sizeof script[0]), state->switches, 8, state->vars, 8);
+ state->vm.host = host;
+ state->vm.user = state;
+ state->who[ADA] = state->who[BO] = -1;
+ kon_scriptStart(&state->vm, 0);
+ return 1;
+}
+
+static void shutdown(kon_program_t *program, void *user)
+{
+ state_t *state = user;
+ int i;
+
+ (void)program;
+ kon_freeFramebuffer(state->back);
+ for (i = 0; i < PEOPLE; i++) kon_freeFramebuffer(state->block[i]);
+}
+
+int main(void)
+{
+ state_t state = {0};
+ kon_programConfig_t config = {0};
+
+ config.title = "script";
+ config.gameWidth = VIEW_WIDTH;
+ config.gameHeight = VIEW_HEIGHT;
+ config.windowWidth = 1280;
+ config.windowHeight = 720;
+ config.user = &state;
+ config.init = init;
+ config.event = event;
+ config.update = update;
+ config.draw = draw;
+ config.shutdown = shutdown;
+ return kon_programRun(&config);
+}