marrub
/
Lithia
Archived
1
0
Fork 0
This repository has been archived on 2023-06-17. You can view files and clone it, but cannot push or open issues/pull-requests.
Lithia/source/Main/w_dialogue.c

675 lines
16 KiB
C

// Copyright © 2016-2017 Graham Sanderson, all rights reserved.
#include "lith_common.h"
#include "lith_world.h"
#include "lith_player.h"
#include "lith_dialogue.h"
#include "lith_file.h"
#include "lith_tokbuf.h"
#define LogTok(tok, s) \
Log("(%i:%i) " s " (%i:\"%s\")", tok->orig.line, tok->orig.colu, tok->type, \
tok->textV ? tok->textV : c"<no string>")
#define LogOri(tok, s) \
Log("(%i:%i) " s, tok->orig.line, tok->orig.colu)
// Extern Objects ------------------------------------------------------------|
extern struct dlgdef *lmvar dlgdefs;
// Static Functions ----------------------------------------------------------|
optargs(1) static int *NextCode(struct pstate *d);
static void GetStatement(struct pstate *d);
// Types ---------------------------------------------------------------------|
enum
{
#define Str(name, str) STR_##name,
#include "lith_dlgstrtable.h"
STR_MAX
};
struct strent {
char const *key, *str;
int name;
size_t keyhash;
struct strent *next, **prev;
};
struct dlgfunc {
char const *name, *args;
int lit[4];
void (*genCode)(struct pstate *d, union arg *argv, int argc);
size_t keyhash;
struct dlgfunc *next, **prev;
};
union arg {
__str s;
int i;
};
struct pstate {
__prop nextCode {call: NextCode(this)}
struct tokbuf tb;
struct dlgdef *def;
};
GDCC_HashMap_Decl(strtable_t, char const *, struct strent)
GDCC_HashMap_Decl(functable_t, char const *, struct dlgfunc)
// Static Objects ------------------------------------------------------------|
static strtable_t stbl;
static functable_t ftbl;
// Static Functions ----------------------------------------------------------|
#define strtable_t_GetKey(o) ((o)->key)
#define strtable_t_GetNext(o) (&(o)->next)
#define strtable_t_GetPrev(o) (&(o)->prev)
#define strtable_t_HashKey(k) (CStrHash(k))
#define strtable_t_HashObj(o) ((o)->keyhash)
#define strtable_t_KeyCmp(l, r) (strcmp(l, r))
GDCC_HashMap_Defn(strtable_t, char const *, struct strent)
#define functable_t_GetKey(o) ((o)->name)
#define functable_t_GetNext(o) (&(o)->next)
#define functable_t_GetPrev(o) (&(o)->prev)
#define functable_t_HashKey(k) (CStrHash(k))
#define functable_t_HashObj(o) ((o)->keyhash)
#define functable_t_KeyCmp(l, r) (strcmp(l, r))
GDCC_HashMap_Defn(functable_t, char const *, struct dlgfunc)
//
// StrName
//
static int StrName(char const *s)
{
ifauto(struct strent *, e, stbl.find(s))
return e->name;
else
return STR_NULL;
}
//
// NextCode
//
static int *NextCode(struct pstate *d)
{
Vec_GrowN(d->def->code, 1, 32);
return &Vec_Next(d->def->code);
}
#define GenCode_Reg(code) \
if(argv[0].s == "a") *d->nextCode() = DCD_##code##_A; \
else if(argv[0].s == "b") *d->nextCode() = DCD_##code##_B; \
else if(argv[0].s == "c") *d->nextCode() = DCD_##code##_C; \
else if(argv[0].s == "d") *d->nextCode() = DCD_##code##_D; \
else
//
// GenCode_Trace
//
static void GenCode_Trace(struct pstate *d, union arg *argv, int argc)
{
GenCode_Reg(TRACE)
{
*d->nextCode() = DCD_TRACE_S;
*d->nextCode() = argv[0].i;
}
}
//
// GenCode_Push
//
static void GenCode_Push(struct pstate *d, union arg *argv, int argc)
{
GenCode_Reg(PUSH)
{
*d->nextCode() = DCD_PUSH_I;
*d->nextCode() = strtoi_str(argv[0].s, null, 0);
}
}
//
// GenCode_Pop
//
static void GenCode_Pop(struct pstate *d, union arg *argv, int argc)
{
GenCode_Reg(POP)
{
struct token *tok = d->tb.reget();
LogOri(tok, "GenCode_Pop: invalid argument");
}
}
#define GenCode_Arith(code) \
static void GenCode_Arith_##code(struct pstate *d, union arg *argv, int argc) \
{ \
GenCode_Reg(code) \
{ \
*d->nextCode() = DCD_##code##_I; \
*d->nextCode() = strtoi_str(argv[0].s, null, 0); \
} \
}
GenCode_Arith(ADD)
GenCode_Arith(SUB)
GenCode_Arith(MUL)
GenCode_Arith(DIV)
GenCode_Arith(MOD)
GenCode_Arith(IOR)
GenCode_Arith(AND)
GenCode_Arith(XOR)
GenCode_Arith(LSH)
GenCode_Arith(RSH)
#undef GenCode_Arith
//
// GenCode_Generic
//
static void GenCode_Generic(struct pstate *d, union arg *argv, int argc)
{
for(int i = 0; i < argc; i++)
*d->nextCode() = argv[i].i;
}
//
// GetCode_Cond
//
// Parses and generates code for a conditional statement.
//
static void GetCode_Cond(struct pstate *d)
{
int code = DCD_NOP;
struct token *tok = d->tb.get();
// Get the code to generate.
if(tok->type == tok_identi)
{
switch(StrName(tok->textV)) {
case STR_item: code = DCD_JNITEM; break;
case STR_class: code = DCD_JNCLASS; break;
default: LogOri(tok, "GetCode_Cond: invalid conditional"); return;
}
}
else
LogTok(tok, "GetCode_Cond: expected identifier");
// Generate code.
int ptr = 0;
tok = d->tb.get();
if(tok->type == tok_identi || tok->type == tok_string)
{
*d->nextCode() = code;
ptr = d->def->codeC;
d->nextCode();
if(code == DCD_JNCLASS)
{
switch(StrName(tok->textV)) {
case STR_Marine: *d->nextCode() = pcl_marine; break;
case STR_CyberMage: *d->nextCode() = pcl_cybermage; break;
case STR_Informant: *d->nextCode() = pcl_informant; break;
default: LogOri(tok, "GetCode_Cond: invalid playerclass type"); return;
}
}
else
*d->nextCode() = (int)Lith_TokStr(tok);
}
else
LogOri(tok, "GetCode_Cond: invalid token in conditional statement");
// Generate statement.
GetStatement(d);
tok = d->tb.get();
if(tok->type == tok_identi && StrName(tok->textV) == STR_else)
{
int tmp = ptr;
// Add jump to end.
*d->nextCode() = DCD_JMP;
ptr = d->def->codeC;
d->nextCode();
// Set original jump target to here.
d->def->codeV[tmp] = d->def->codeC;
// Generate statement.
GetStatement(d);
}
else
d->tb.unget();
// Set the pointer in the generated code to be after the statement.
if(ptr) d->def->codeV[ptr] = d->def->codeC;
}
//
// GetCode_Option
//
// Parses and generates code for an option statement.
//
static void GetCode_Option(struct pstate *d)
{
struct token *tok = d->tb.get();
// Generate code.
int ptr = 0;
if(tok->type == tok_identi || tok->type == tok_string)
{
*d->nextCode() = DCD_PUTOPT;
ptr = d->def->codeC;
d->nextCode();
*d->nextCode() = (int)Lith_TokStr(tok);
}
else
LogOri(tok, "GetCode_Option: invalid option parameter");
// Generate statement.
GetStatement(d);
// Set the pointer in the generated code to be after the statement.
if(ptr) d->def->codeV[ptr] = d->def->codeC;
}
//
// GetCode_Exec
//
// Parses and generates code for an exec statement.
//
static void GetCode_Exec(struct pstate *d)
{
*d->nextCode() = DCD_TRMWAIT;
GetStatement(d);
}
//
// GetCode_Generic
//
// Parses and generates code for a generic statement.
//
static void GetCode_Generic(struct pstate *d)
{
struct token *tok = d->tb.reget();
LogDebug(log_dlg, "call: %s", tok->textV);
// Get the function to generate.
struct dlgfunc const *func = ftbl.find(tok->textV);
if(!func) {
LogOri(tok, "GetCode_Generic: invalid function in dialogue code");
return;
}
// Get arguments.
union arg argv[8] = {};
int argc = 0;
int lit = 0;
while(func->args[argc])
{
if(func->args[argc] == 'L')
{
argv[argc++].i = func->lit[lit++];
continue;
}
if(!(tok = d->tb.get())->textV)
{
LogTok(tok, "GetCode_Generic: invalid token in argument list");
return;
}
switch(func->args[argc])
{
case 'I': argv[argc++].i = strtoi(tok->textV, null, 0); break;
case 'S': argv[argc++].s = Lith_TokStr(tok); break;
}
LogDebug(log_dlg, "arg %i: %s", argc, tok->textV);
if(!d->tb.drop(tok_comma) || d->tb.drop(tok_semico))
break;
}
// Fill in unfinished arguments.
while(func->args[argc])
{
switch(func->args[argc])
{
case 'I': argv[argc++].i = 0; break;
case 'S': argv[argc++].s = ""; break;
case 'L': argv[argc++].i = func->lit[lit++]; break;
}
LogDebug(log_dlg, "arg %i emptied", argc);
}
func->genCode(d, argv, argc);
}
//
// GetCode_Text
//
static void GetCode_Text(struct pstate *d, struct token *tok, int code)
{
*d->nextCode() = code;
*d->nextCode() = (int)Lith_TokStr(tok);
}
//
// GetCode_Line
//
// Parse and generate a line of code.
//
static void GetCode_Line(struct pstate *d)
{
struct token *tok = d->tb.get();
switch(tok->type)
{
case tok_identi:
LogDebug(log_dlg, "GetCode_Line: %s", tok->textV);
switch(StrName(tok->textV)) {
case STR_if: GetCode_Cond (d); break;
case STR_option: GetCode_Option (d); break;
case STR_exec: GetCode_Exec (d); break;
default: GetCode_Generic(d); break;
}
break;
case tok_quote: GetCode_Text(d, d->tb.reget(), DCD_ADDTEXT); break;
case tok_dollar: GetCode_Text(d, d->tb. get(), DCD_ADDTEXTLOCAL); break;
case tok_semico:
case tok_eof:
break;
default:
LogTok(tok, "GetCode_Line: invalid token in line");
}
}
//
// GetBlock
//
// Parse and generate a block statement.
//
static void GetBlock(struct pstate *d)
{
while(!d->tb.drop(tok_bracec) && !d->tb.drop(tok_eof))
GetStatement(d);
}
//
// GetConcatBlock
//
// Parse and generate a concat block statement.
//
static void GetConcatBlock(struct pstate *d)
{
*d->nextCode() = DCD_CONCAT;
while(!d->tb.drop(tok_at2) && !d->tb.drop(tok_eof))
GetStatement(d);
*d->nextCode() = DCD_CONCATEND;
}
//
// GetStatement
//
// Parse and generate a statement.
//
static void GetStatement(struct pstate *d)
{
if(d->tb.drop(tok_braceo)) GetBlock(d);
else if(d->tb.drop(tok_at2)) GetConcatBlock(d);
else GetCode_Line(d);
}
//
// SetupDialogue
//
static void SetupDialogue(struct pstate *d, int num)
{
struct dlgdef *last = d->def;
d->def = Salloc(struct dlgdef);
d->def->num = num;
if(!last) dlgdefs = d->def;
else last->next = d->def;
LogDebug(log_dlg, "set up dialogue %i", num);
}
//
// GetDecl_Dialogue
//
static void GetDecl_Dialogue(struct pstate *d)
{
struct token *tok = d->tb.get();
if(tok->type == tok_number) {
SetupDialogue(d, strtoi(tok->textV, null, 0));
LogDebug(log_dlg, "\n---\ndialogue %i (%i)\n---",
d->def->num, d->def->codeC);
} else {
LogOri(tok, "GetDecl_Dialogue: invalid dialogue number token");
}
}
//
// GetDecl_Terminal
//
static void GetDecl_Terminal(struct pstate *d)
{
struct token *tok = d->tb.get();
if(tok->type == tok_number) {
SetupDialogue(d, -strtoi(tok->textV, null, 0));
LogDebug(log_dlg, "\n---\nterminal %i (%i)\n---",
-d->def->num, d->def->codeC);
} else {
LogOri(tok, "GetDecl_Terminal: invalid terminal number token");
}
}
//
// SetupPage
//
static void SetupPage(struct pstate *d, int num)
{
d->def->pages[num] = d->def->codeC;
LogDebug(log_dlg, "--- page %i (%i)", num, d->def->codeC);
}
//
// GetDecl_Page
//
static void GetDecl_Page(struct pstate *d)
{
struct token *tok = d->tb.get();
if(tok->type == tok_number)
SetupPage(d, strtoi(tok->textV, null, 0));
else
LogOri(tok, "GetDecl_Page: invalid page number token");
GetStatement(d);
*d->nextCode() = DCD_DLGWAIT;
*d->nextCode() = DCD_DIE;
}
//
// GetDecl_TrmPage
//
static void GetDecl_TrmPage(struct pstate *d, int num)
{
SetupPage(d, num);
GetStatement(d);
*d->nextCode() = DCD_TRMWAIT;
*d->nextCode() = DCD_DIE;
}
// Extern Functions ----------------------------------------------------------|
//
// Lith_GSInit_Dialogue
//
// Loads all string indices into the global stbl, and all function
// prototypes into the global ftbl.
//
void Lith_GSInit_Dialogue(void)
{
#pragma GDCC STRENT_LITERAL OFF
static struct dlgfunc funcs[] = {
{"nop", "L", DCD_NOP},
{"push", "S", .genCode = GenCode_Push},
{"pop", "S", .genCode = GenCode_Pop},
{"add", "S", .genCode = GenCode_Arith_ADD},
{"sub", "S", .genCode = GenCode_Arith_SUB},
{"mul", "S", .genCode = GenCode_Arith_MUL},
{"div", "S", .genCode = GenCode_Arith_DIV},
{"mod", "S", .genCode = GenCode_Arith_MOD},
{"ior", "S", .genCode = GenCode_Arith_IOR},
{"and", "S", .genCode = GenCode_Arith_AND},
{"xor", "S", .genCode = GenCode_Arith_XOR},
{"lsh", "S", .genCode = GenCode_Arith_LSH},
{"rsh", "S", .genCode = GenCode_Arith_RSH},
{"die", "L", DCD_DIE},
{"exit", "L", DCD_DIE},
{"page", "LI", DCD_JPAGE},
{"script", "LIIIII", DCD_SCRIPTI},
{"scriptnamed", "LSIIII", DCD_SCRIPTS},
{"trace", "S", .genCode = GenCode_Trace},
{"intralevelteleport", "LI", DCD_TELEPORT_INTRALEVEL},
{"interlevelteleport", "LI", DCD_TELEPORT_INTERLEVEL},
{"text", "LS", DCD_SETTEXT},
{"local", "LS", DCD_SETTEXTLOCAL},
{"addtext", "LS", DCD_ADDTEXT},
{"addlocal", "LS", DCD_ADDTEXTLOCAL},
{"setstr", "LIS", DCD_SETSTRING},
{"name", "LLS", DCD_SETSTRING, DSTR_NAME},
{"icon", "LLS", DCD_SETSTRING, DSTR_ICON},
{"remote", "LLS", DCD_SETSTRING, DSTR_REMOTE},
{"concat", "L", DCD_CONCAT},
{"endconcat", "L", DCD_CONCATEND},
{"dlgwait", "L", DCD_DLGWAIT},
{"logon", "LS", DCD_LOGON},
{"logoff", "LS", DCD_LOGOFF},
{"info", "L", DCD_INFO},
{"pict", "LS", DCD_PICT},
{"trmwait", "L", DCD_TRMWAIT},
};
static struct strent strs[] = {
#define Str(name, str) {#name, #str},
#include "lith_dlgstrtable.h"
};
strtable_t_ctor(&stbl, countof(strs), 1);
for(int i = 0; i < countof(strs); i++) {
strs[i].keyhash = CStrHash(strs[i].key);
strs[i].name = i;
stbl.insert(&strs[i]);
}
functable_t_ctor(&ftbl, countof(funcs), 1);
for(int i = 0; i < countof(funcs); i++) {
funcs[i].keyhash = CStrHash(funcs[i].name);
if(!funcs[i].genCode) funcs[i].genCode = GenCode_Generic;
ftbl.insert(&funcs[i]);
}
}
//
// Lith_LoadMapDialogue
//
void Lith_LoadMapDialogue(void)
{
// Free any previous dialogue definitions.
if(dlgdefs)
{
for(struct dlgdef *def = dlgdefs; def;) {
struct dlgdef *next = def->next;
Vec_Clear(def->code);
Dalloc(def);
def = next;
}
Dalloc(dlgdefs);
dlgdefs = null;
}
struct pstate d = {{
.bbeg = 4, .bend = 10,
.fp = W_Open(StrParam("LITH_DLG_SCRIPT_%tS", PRINTNAME_LEVEL), c"r")
}};
if(!d.tb.fp) return;
d.tb.ctor();
for(struct token *tok; (tok = d.tb.get())->type != tok_eof;)
{
if(tok->type != tok_identi) {
LogTok(tok, "Lith_LoadMapDialogue: invalid toplevel token");
break;
}
switch(StrName(tok->textV))
{
case STR_dialogue: GetDecl_Dialogue(&d); break;
case STR_page: GetDecl_Page (&d); break;
case STR_terminal: GetDecl_Terminal(&d); break;
case STR_failure: GetDecl_TrmPage (&d, DTRMPAGE_FAILURE); break;
case STR_finished: GetDecl_TrmPage (&d, DTRMPAGE_FINISHED); break;
case STR_unfinished: GetDecl_TrmPage (&d, DTRMPAGE_UNFINISHED); break;
default:
Log("Lith_LoadMapDialogue: invalid identifier \"%s\"", tok->textV);
goto done;
}
}
done:
d.tb.dtor();
fclose(d.tb.fp);
if(world.dbgLevel & log_dlg)
{
static __str const dcdnames[] = {
#define DCD(name) #name,
#include "lith_dialogue.h"
};
for(struct dlgdef *def = dlgdefs; def; def = def->next) {
Log("Dumping code for script %i...", def->num);
for(int i = 0; i < def->codeC; i++)
Log("%i (%S)", def->codeV[i], def->codeV[i] < countof(dcdnames)
? dcdnames[def->codeV[i]] : "<none>");
}
Log("Done.");
}
}
// EOF