1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
/* SPDX-License-Identifier: MIT */
/*
* konscript.h
*
* Copyright (c) 2026 KonAki
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef KONSCRIPT_H
#define KONSCRIPT_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*** definition ***/
/* a script is a flat list of commands and jumps go to a place in the list. no nesting, so a converter can write one
(or write it as C) without a parser, and a person can write one by hand. the program does the talking and the
showing: say, menus and anything else the script cannot know about are handed over and the script waits */
typedef enum {
KON_OP_END,
KON_OP_SAY, /* a: who (your own number, -1 for nobody), text */
KON_OP_MENU, /* a: how many OPTION commands follow */
KON_OP_OPTION, /* a: where to go, b: switch that must be on to show it (-1 always), text */
KON_OP_JUMP, /* a: where */
KON_OP_SWITCH, /* a: switch, b: 0 off, 1 on, 2 flip */
KON_OP_VAR, /* a: variable, b: KON_SET.., c: number, or KON_VARIABLE + a variable */
KON_OP_IFSWITCH, /* a: switch, b: wanted 0 or 1, c: where to go if it is not */
KON_OP_IFVAR, /* a: variable, b: KON_IS.., c: number or KON_VARIABLE + a variable, d: where to go if not */
KON_OP_WAIT, /* a: milliseconds */
KON_OP_HOST, /* a: your own command number, b, c, d, text: yours to use */
KON_OP_CALL, /* a: where, comes back at the RETURN */
KON_OP_RETURN
} kon_op_t;
enum { KON_SET, KON_ADD, KON_SUB, KON_MUL, KON_DIV, KON_MOD };
enum { KON_IS_EQ, KON_IS_NE, KON_IS_GE, KON_IS_LE, KON_IS_GT, KON_IS_LT };
/* add a variable number to this to use that variable where a command wants a number */
#define KON_VARIABLE 0x40000000
typedef struct kon_cmd {
int op, a, b, c, d;
const char *text;
} kon_cmd_t;
/* so a script written by hand or by a converter reads as a list */
#define KON_END() {KON_OP_END, 0, 0, 0, 0, NULL}
#define KON_SAY(who, text) {KON_OP_SAY, who, 0, 0, 0, text}
#define KON_MENU(count) {KON_OP_MENU, count, 0, 0, 0, NULL}
#define KON_OPTION(text, where, needs) {KON_OP_OPTION, where, needs, 0, 0, text}
#define KON_JUMP(where) {KON_OP_JUMP, where, 0, 0, 0, NULL}
#define KON_SWITCH(id, how) {KON_OP_SWITCH, id, how, 0, 0, NULL}
#define KON_VAR(id, how, n) {KON_OP_VAR, id, how, n, 0, NULL}
#define KON_IFSWITCH(id, want, elseWhere) {KON_OP_IFSWITCH, id, want, elseWhere, 0, NULL}
#define KON_IFVAR(id, how, n, elseWhere) {KON_OP_IFVAR, id, how, n, elseWhere, NULL}
#define KON_WAIT(ms) {KON_OP_WAIT, ms, 0, 0, 0, NULL}
#define KON_HOST(id, b, c, d, text) {KON_OP_HOST, id, b, c, d, text}
#define KON_CALL(where) {KON_OP_CALL, where, 0, 0, 0, NULL}
#define KON_RETURN() {KON_OP_RETURN, 0, 0, 0, 0, NULL}
typedef enum {
KON_SCRIPT_DONE,
KON_SCRIPT_RUNNING, /* started, kon_scriptRun has not stopped it yet */
KON_SCRIPT_SAY, /* show kon_scriptText, then kon_scriptResume */
KON_SCRIPT_MENU, /* show the options, then kon_scriptChoose */
KON_SCRIPT_WAIT, /* kon_scriptUpdate counts it down */
KON_SCRIPT_HOST, /* the host function wanted to wait, kon_scriptResume when it is over */
KON_SCRIPT_ERROR /* bad jump, calls too deep, or going round and round without stopping */
} kon_scriptStatus_t;
#define KON_SCRIPT_MAX_OPTIONS 16
#define KON_SCRIPT_MAX_CALLS 64
struct kon_script;
typedef int (*kon_hostFn)(struct kon_script *vm, const kon_cmd_t *cmd, void *user);
typedef struct kon_script {
const kon_cmd_t *cmds;
int count;
uint8_t *switches;
int switchCount;
int32_t *vars;
int varCount;
int yieldAtLimit; /* 0 makes a script that runs 100000 commands without waiting an ERROR, 1 makes kon_scriptRun give back RUNNING and carry on next call, like RPG Maker does */
kon_hostFn host; /* returns nonzero to make the script wait for kon_scriptResume */
void *user;
kon_scriptStatus_t status;
int pc;
int cmd; /* the command that is being waited on */
float wait;
int stack[KON_SCRIPT_MAX_CALLS], depth;
int options[KON_SCRIPT_MAX_OPTIONS], optionCount;
} kon_script_t;
/*** script ***/
/* the switches and variables are yours, they start however you left them, and a number past the end reads as 0 and is not written */
void kon_scriptInit(kon_script_t *vm, const kon_cmd_t *cmds, int count, uint8_t *switches, int switchCount, int32_t *vars, int varCount);
void kon_scriptStart(kon_script_t *vm, int where);
/* goes until it has to wait for something and says what, call it each frame (it does nothing while waiting) */
kon_scriptStatus_t kon_scriptRun(kon_script_t *vm);
void kon_scriptUpdate(kon_script_t *vm, float dt);
void kon_scriptResume(kon_script_t *vm);
const kon_cmd_t *kon_scriptCurrent(const kon_script_t *vm);
int kon_scriptOptionCount(const kon_script_t *vm);
const char *kon_scriptOption(const kon_script_t *vm, int i);
void kon_scriptChoose(kon_script_t *vm, int i);
int kon_scriptSwitch(const kon_script_t *vm, int id);
int32_t kon_scriptVar(const kon_script_t *vm, int id);
void kon_scriptSetSwitch(kon_script_t *vm, int id, int on);
void kon_scriptSetVar(kon_script_t *vm, int id, int32_t value);
/*** text scripts ***/
/* the same commands as text, so a script can be written, read and changed by hand and a converter can write one.
one command a line, # starts a comment, labels are names to jump to:
start:
say ada "Morning."
host enter 0 1
menu
option "Sorry." sorry
option "About the key..." key if s0
sorry:
set v1 = 0
if v1 < 2 else done
jump start
done:
end
say who "text" | menu | option "text" label [if sN] | jump label | call label | return | end | wait ms
on sN | off sN | flip sN | set vN (= += -= *= /= %=) number-or-vM
if sN on|off else label | if vN (== != >= <= > <) number-or-vM else label
host name-or-number [b [c [d]]] ["text"]
who and the host name are numbers, or names from the lists you give. use - for nobody. in strings, \" \\ \n and \t work */
#define KON_SCRIPT_LABEL 32
typedef struct kon_scriptLabel {
char name[KON_SCRIPT_LABEL];
int at;
} kon_scriptLabel_t;
typedef struct kon_scriptNames {
const char *const *speakers;
int speakerCount;
const char *const *hosts;
int hostCount;
} kon_scriptNames_t;
typedef struct kon_scriptBuild {
const kon_scriptNames_t *names; /* can be NULL */
kon_cmd_t *cmds;
int maxCmds;
char *pool; /* where the strings go, the commands point into it so keep it as long as them */
int poolSize;
kon_scriptLabel_t *labels;
int maxLabels;
int *hash; /* optional, makes looking up labels fast for big scripts. hashSize is a power of two, at least twice maxLabels */
int hashSize;
int count, labelCount, poolUsed; /* what it made */
int errorLine; /* 0 if no error */
char error[64];
} kon_scriptBuild_t;
/* 1 if it worked, 0 and errorLine, error if not */
int kon_scriptParse(kon_scriptBuild_t *b, const char *source);
int kon_scriptFind(const kon_scriptBuild_t *b, const char *label); /* where a label is, or -1 */
#ifdef __cplusplus
}
#endif
#endif /* KONSCRIPT_H */
|