1 /**
2 * DinkC script engine
3
4 * Copyright (C) 1997, 1998, 1999, 2002, 2003 Seth A. Robinson
5 * Copyright (C) 2005, 2006 Dan Walma
6 * Copyright (C) 2005, 2007, 2008, 2009, 2011 Sylvain Beucler
7
8 * This file is part of GNU FreeDink
9
10 * GNU FreeDink is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 3 of the
13 * License, or (at your option) any later version.
14
15 * GNU FreeDink is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see
22 * <http://www.gnu.org/licenses/>.
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <strings.h> /* compare */
33 #include <xalloc.h>
34
35 #include "gettext.h"
36 #define _(String) gettext (String)
37
38 #include "dinkc.h"
39 #include "dinkc_bindings.h"
40 #include "game_engine.h"
41 #include "input.h"
42 #include "paths.h"
43 #include "str_util.h"
44 #include "log.h"
45
46 int returnint = 0;
47 int bKeepReturnInt = 0;
48 char returnstring[200];
49 /* Used to tell decipher_string about the currently selected savegame
50 in a choice menu; also abuse to tell which key is selected in
51 joystick remapping */
52 unsigned short decipher_savegame = 0;
53
54 #define MAX_CALLBACKS 100
55 struct call_back
56 {
57 int owner;
58 /*bool*/int active;
59 int type;
60 char name[20];
61 int offset;
62 long min, max;
63 int lifespan;
64 unsigned long timer;
65 };
66 static struct call_back callback[MAX_CALLBACKS];
67 /* TODO: Used 1->100 in the game, should it be MAX_CALLBACKS+1 ? */
68
69 /* DinkC script buffer */
70 static char *rbuf[MAX_SCRIPTS]; //pointers to buffers we may need
71
72 /* Number of reserved ASCII indexes in .d BPE compression format */
73 #define NB_PAIRS_MAX 128
74
75
76 struct refinfo *rinfo[MAX_SCRIPTS];
77
78
79 /**
80 * Decompress a .d DinkC script; also clean newlines. Check
81 * contrib/d2c.c for more explanation about the decompression process.
82 */
decompress(FILE * in,int script)83 static void decompress(FILE *in, int script)
84 {
85 int step = 512;
86 int nb_read = 0;
87 rbuf[script] = xmalloc(step);
88 rbuf[script][0] = '\0';
89
90 unsigned char stack[NB_PAIRS_MAX+1], pairs[NB_PAIRS_MAX][2];
91 short c, top = -1;
92 int nb_pairs = 0;
93
94 /* Check for optional pair count and pair table */
95 if ((c = fgetc(in)) > 127)
96 {
97 /* Read pairs table */
98 nb_pairs = c - 128;
99 int i, j;
100 for (i = 0; i < nb_pairs; i++)
101 {
102 for (j = 0; j < 2; j++)
103 {
104 int c = fgetc(in);
105 if (c == EOF)
106 {
107 log_error("decompress: invalid header: truncated pair table");
108 free(rbuf[script]);
109 rbuf[script] = NULL;
110 return;
111 }
112 if (c > i+128)
113 {
114 log_error("decompress: invalid header: reference to a pair that is not registered yet");
115 free(rbuf[script]);
116 rbuf[script] = NULL;
117 return;
118 }
119 pairs[i][j] = c;
120 }
121 }
122 }
123 else
124 {
125 /* Non-compressed file, put back the character we read */
126 ungetc(c, in);
127 }
128
129 for (;;)
130 {
131 /* Pop byte from stack or read byte from file */
132 if (top >= 0)
133 c = stack[top--];
134 else if ((c = fgetc(in)) == EOF)
135 break;
136
137 /* Push pair on stack or output byte to file */
138 if (c > 127)
139 {
140 if ((c-128) >= nb_pairs)
141 {
142 log_error("decompress: invalid body: references non-existent pair");
143 break;
144 }
145 stack[++top] = pairs[c-128][1];
146 stack[++top] = pairs[c-128][0];
147 }
148 else
149 {
150 rbuf[script][nb_read] = c;
151 nb_read++;
152 if ((nb_read % step) == 0)
153 rbuf[script] = xrealloc(rbuf[script], nb_read + step);
154 }
155 }
156 rinfo[script]->end = nb_read;
157 rbuf[script][nb_read] = '\0'; /* safety */
158 rbuf[script] = xrealloc(rbuf[script], nb_read+1);
159 }
160
decompress_nocomp(FILE * in,int script)161 static void decompress_nocomp(FILE *in, int script)
162 {
163 int step = 512;
164 int nb_read = 0;
165 rbuf[script] = xmalloc(step);
166 rbuf[script][0] = '\0';
167
168 int c;
169 while ((c = getc(in)) != EOF)
170 {
171 rbuf[script][nb_read] = c;
172 nb_read++;
173 if ((nb_read % step) == 0)
174 rbuf[script] = xrealloc(rbuf[script], nb_read + step);
175 }
176 rinfo[script]->end = nb_read;
177 rbuf[script][nb_read] = '\0'; /* safety */
178 rbuf[script] = xrealloc(rbuf[script], nb_read+1);
179 }
180
181
182 /**
183 * Only load game metadata (timetime). Used when displaying the list
184 * of saved games (see decipher_string).
185 */
load_game_small(int num,char line[196],int * mytime)186 static /*bool*/int load_game_small(int num, char line[196], int *mytime)
187 {
188 FILE *f = paths_savegame_fopen(num, "rb");
189 if (f == NULL)
190 {
191 log_info("Couldn't quickload save game %d", num);
192 return /*false*/0;
193 }
194 else
195 {
196 //int version = read_lsb_int(f);
197 fseek(f, 4, SEEK_CUR);
198
199 fread(line, 196, 1, f);
200 line[195] = '\0';
201 *mytime = read_lsb_int(f);
202 fclose(f);
203
204 return /*true*/1;
205 }
206 }
207
208
209 /**
210 * Load script from 'filename', save it in the first available script
211 * slot, attach to game sprite #'sprite' if 'set_sprite' is 1.
212 **/
load_script(char filename[15],int sprite,int set_sprite)213 int load_script(char filename[15], int sprite, /*bool*/int set_sprite)
214 {
215 char temp[100];
216 int script = 0;
217 FILE *in = NULL;
218 /*bool*/int comp = /*false*/0;
219
220 log_info("LOADING %s", filename);
221
222 sprintf(temp, "story/%s.d", filename);
223 in = paths_dmodfile_fopen(temp, "rb");
224 if (in == NULL)
225 {
226 sprintf(temp, "story/%s.c", filename);
227 in = paths_dmodfile_fopen(temp, "rb");
228 if (in == NULL)
229 {
230 sprintf(temp, "story/%s.d", filename);
231 in = paths_fallbackfile_fopen(temp, "rb");
232 if (in == NULL)
233 {
234 sprintf(temp, "story/%s.c", filename);
235 in = paths_fallbackfile_fopen(temp, "rb");
236 if (in == NULL)
237 {
238 log_warn("Script %s not found. (checked for .C and .D) (requested by %d?)", temp, sprite);
239 return 0;
240 }
241 }
242 }
243 }
244
245 strtoupper(temp);
246 log_debug("Temp thingie is %c", temp[strlen(temp)-1]);
247 if (temp[strlen(temp)-1] == 'D')
248 comp = 1;
249 else
250 comp = 0;
251
252 {
253 int k = 1;
254 for (; k < MAX_SCRIPTS; k++)
255 if (rbuf[k] == NULL)
256 break;
257 script = k;
258 }
259
260 if (script == MAX_SCRIPTS)
261 {
262 log_error("Couldn't find unused buffer for script.");
263 fclose(in);
264 return 0;
265 }
266
267 log_info("Loading script %s.. (slot %d)", temp, script);
268 rinfo[script] = XZALLOC(struct refinfo);
269 if (rinfo[script] == NULL)
270 {
271 log_error("Couldn't allocate rscript %d.", script);
272 return 0;
273 }
274 memset(rinfo[script], 0, sizeof(struct refinfo));
275 /* For clarity: */
276 rinfo[script]->current = 0;
277 rinfo[script]->cur_line = 1;
278 rinfo[script]->cur_col = 0;
279 rinfo[script]->debug_line = 1;
280
281
282 if (comp)
283 {
284 log_debug("Decompressing...");
285 decompress(in, script);
286 }
287 else
288 {
289 log_debug("Reading from disk...");
290 decompress_nocomp(in, script);
291 }
292 fclose(in);
293
294 if (rbuf[script] == NULL)
295 {
296 log_error("Couldn't allocate rbuff %d.", script);
297 free(rinfo[script]);
298 rinfo[script] = NULL;
299 return 0;
300 }
301
302 rinfo[script]->name = strdup(filename);
303 rinfo[script]->sprite = sprite;
304
305 if (set_sprite && sprite != 0 && sprite != 1000)
306 spr[sprite].script = script;
307
308 return script;
309 }
310
311
dinkc_execute_one_liner(char * line)312 int dinkc_execute_one_liner(char* line)
313 {
314 /* Find available script slot */
315 int k = 1;
316 for (k = 1; k < MAX_SCRIPTS; k++)
317 if (rbuf[k] == NULL)
318 break;
319
320 if (k < MAX_SCRIPTS)
321 {
322 rinfo[k] = XZALLOC(struct refinfo);
323 rinfo[k]->name = xmalloc(1);
324 rinfo[k]->name[0] = '\0';
325 rinfo[k]->sprite = 1000; /* survice screen change */
326 rinfo[k]->level = 1; /* skip 'void main(void) {' parsing */
327 rbuf[k] = (char*) malloc(255);
328 strcpy(rbuf[k], line);
329 process_line(k, rbuf[k], 0);
330 return returnint;
331 }
332 else
333 return -1;
334 }
335
336
337 /**
338 * Remove leading spaces by shifting 'str' to the left, as much as
339 * there is leading spaces.
340 */
strip_beginning_spaces(char * str)341 void strip_beginning_spaces(char *str)
342 {
343 char *pc = str;
344 int diff = 0;
345 /* int i; */
346
347 /* Find first non-space character (pos) */
348 while (*pc == ' ')
349 pc++;
350 diff = pc - str;
351
352 /* Shift string to the left from pos */
353 /* Don't use str(str, pc) to avoid memory overlap */
354 while (*pc != '\0')
355 {
356 *(pc - diff) = *pc;
357 pc++;
358 }
359 *(pc - diff) = '\0';
360 }
361
362
363
364 /**
365 * Locate a procedure (such as "void hit()")
366 */
locate(int script,char * lookup_proc)367 /*bool*/int locate(int script, char* lookup_proc)
368 {
369 if (rinfo[script] == NULL)
370 return 0;
371
372 int save_current = rinfo[script]->current;
373 int save_cur_line = rinfo[script]->cur_line;
374 int save_cur_col = rinfo[script]->cur_col;
375 int save_debug_line = rinfo[script]->debug_line;
376 rinfo[script]->current = 0;
377 rinfo[script]->cur_line = 1;
378 rinfo[script]->cur_col = 0;
379 rinfo[script]->debug_line = 1;
380
381 char* line = NULL;
382 char* word = NULL;
383
384 while((line = read_next_line(script)) != NULL)
385 {
386 strip_beginning_spaces(line);
387
388 int is_proc = 0;
389 word = get_word(line, 1);
390 if (compare(word, "VOID"))
391 is_proc = 1;
392 free(word);
393 if (is_proc)
394 {
395 char* cur_proc = NULL;
396 word = get_word(line, 2);
397 cur_proc = separate_string(word, 1, '(');
398 free(word);
399
400 int is_right_proc = 0;
401 if (compare(cur_proc, lookup_proc))
402 is_right_proc = 1;
403 free(cur_proc);
404
405 if (is_right_proc)
406 {
407 //clean up vars so it is ready to run
408 if (rinfo[script]->sprite != 1000)
409 {
410 spr[rinfo[script]->sprite].move_active = 0;
411 if (dversion >= 108)
412 spr[rinfo[script]->sprite].move_nohard = 0;
413 }
414 rinfo[script]->skipnext = /*false*/0;
415 rinfo[script]->onlevel = 0;
416 rinfo[script]->level = 0;
417
418 free(line);
419 return 1;
420 //this is desired proc
421 }
422 }
423 free(line);
424 }
425
426 // Not found, restoring position
427 rinfo[script]->current = save_current;
428 rinfo[script]->cur_line = save_cur_line;
429 rinfo[script]->cur_col = save_cur_col;
430 rinfo[script]->debug_line = save_debug_line;
431 return 0;
432 }
433
434 /**
435 * Look for the 'label' label (e.g. 'loop:'), that is used by a "goto"
436 * instruction. This sets the script->current field appropriately.
437 **/
locate_goto(char * expr,int script)438 /*bool*/int locate_goto(char* expr, int script)
439 {
440 replace_norealloc(";", "", expr);
441 char* label = xmalloc(strlen(expr) + 1 + 1);
442 sprintf(label, "%s:", expr);
443
444 char* line = NULL;
445 rinfo[script]->current = 0;
446 rinfo[script]->cur_line = 1;
447 while ((line = read_next_line(script)) != NULL)
448 {
449 strip_beginning_spaces(line);
450
451 int is_right_label = 0;
452 char* word = get_word(line, 1);
453 replace_norealloc("\n", "", word);
454 if (compare(word, label))
455 is_right_label = 1;
456 free(word);
457
458 if (is_right_label)
459 {
460 log_debug("Found goto : Line is %s, word is %s.", line, label);
461
462 rinfo[script]->skipnext = /*false*/0;
463 rinfo[script]->onlevel = 0;
464 rinfo[script]->level = 0;
465
466 free(label);
467 free(line);
468 return 1;
469 //this is desired label
470 }
471 free(line);
472 }
473
474 log_warn("%s: cannot goto %s", rinfo[script]->name, label);
475 free(label);
476 return 0;
477 }
478
479 /**
480 * v1.07-style scope. This function is buggy: the first memory slot
481 * has precedence (independently of local/global scope).
482 *
483 * Return -1 if not found, slot index >1 if found. Slot 0 isn't
484 * currently used by the engine.
485 */
search_var_with_this_scope_107(char * variable,int var_scope)486 int search_var_with_this_scope_107(char* variable, int var_scope)
487 {
488 int i;
489 for (i = 1; i < MAX_VARS; i ++)
490 if (play.var[i].active == 1
491 && ((play.var[i].scope == DINKC_GLOBAL_SCOPE) || (play.var[i].scope == var_scope))
492 && (compare(play.var[i].name, variable)))
493 return i;
494 return -1; /* not found */
495 }
496
497 /**
498 * v1.08-style scope: local variables are searched before global
499 * variables.
500 *
501 * Return -1 if not found, slot index >1 if found. Slot 0 isn't
502 * currently used by the engine.
503 */
search_var_with_this_scope_108(char * variable,int var_scope)504 int search_var_with_this_scope_108(char* variable, int var_scope)
505 {
506 int search_scope[2];
507 search_scope[0] = var_scope; /* first local scope */
508 search_scope[1] = DINKC_GLOBAL_SCOPE; /* then global scope */
509
510 int i;
511 for (i = 0; i < 2; i++)
512 {
513 //We'll start going through every var, starting at one
514 int v;
515 for (v = 1; v < MAX_VARS; v++)
516 {
517 //Okay... make sure the var is active,
518 //The scope should match the script,
519 //Then make sure the name is the same.
520 if (play.var[v].active
521 && play.var[v].scope == search_scope[i]
522 && compare (play.var[v].name, variable))
523 return v;
524 }
525 }
526 return -1;
527 }
528
529 /**
530 *
531 */
search_var_with_this_scope(char * variable,int scope)532 int search_var_with_this_scope(char* variable, int scope)
533 {
534 if (dversion >= 108)
535 return search_var_with_this_scope_108(variable, scope);
536 return search_var_with_this_scope_107(variable, scope);
537 }
538
539 /**
540 * Expand 'variable' in the scope of 'script' and return the integer
541 * value. Only used in function 'get_parms'.
542 */
decipher(char * variable,int script)543 long decipher(char* variable, int script)
544 {
545 // Special vars: ¤t_sprite and ¤t_script
546 if (compare(variable, "¤t_sprite"))
547 return rinfo[script]->sprite;
548 if (compare(variable, "¤t_script"))
549 return script;
550
551 //v1.08 special variables.
552 if (dversion >= 108)
553 {
554 if (compare(variable, "&return"))
555 return returnint;
556 if (compare(variable, "&arg1"))
557 return rinfo[script]->arg1;
558 if (compare(variable, "&arg2"))
559 return rinfo[script]->arg2;
560 if (compare(variable, "&arg3"))
561 return rinfo[script]->arg3;
562 if (compare(variable, "&arg4"))
563 return rinfo[script]->arg4;
564 if (compare(variable, "&arg5"))
565 return rinfo[script]->arg5;
566 if (compare(variable, "&arg6"))
567 return rinfo[script]->arg6;
568 if (compare(variable, "&arg7"))
569 return rinfo[script]->arg7;
570 if (compare(variable, "&arg8"))
571 return rinfo[script]->arg8;
572 if (compare(variable, "&arg9"))
573 return rinfo[script]->arg9;
574 }
575
576 // Check in local and global variables
577 int i = search_var_with_this_scope(variable, script);
578 if (i != -1)
579 return play.var[i].var;
580 else
581 return 0; // compatibility
582 }
583
584
585 /**
586 * Replace all variables in a string; try longest variables
587 * first. Known bug: may replace shorter variables (e.g. &gold instead
588 * of &golden).
589 */
var_replace_107(char ** line_p,int scope)590 void var_replace_107(char** line_p, int scope)
591 {
592 char crap[20];
593 int i;
594 for (i = 1; i < MAX_VARS; i ++)
595 if ((play.var[i].active == 1)
596 && ((play.var[i].scope == DINKC_GLOBAL_SCOPE) || (play.var[i].scope == scope)))
597 {
598 sprintf(crap, "%d", play.var[i].var);
599 replace(play.var[i].name, crap, line_p);
600 }
601 }
602
603 /**
604 * Replace all variables in a string; try longest variables first.
605 *
606 * Possible improvements:
607 *
608 * - Copy play.var[] and sort it by variable length (and avoid the
609 * recursion)
610 *
611 * - find vars in the string and replace them as-needed (requires
612 * understanding what exactly is an end-of-variable delimiter, if
613 * such a thing exists)
614 */
var_replace_108(int i,int script,char ** line_p,char * prevar)615 void var_replace_108(int i, int script, char** line_p, char *prevar)
616 {
617 while (i < MAX_VARS)
618 {
619 //First, make sure the variable is active.
620 //Then, make sure it is in scope,
621 //Then, see if the variable name is in the line
622 //Then, prevar is null, or if prevar isn't null, see if current variable starts with prevar
623 if (play.var[i].active
624 && i == search_var_with_this_scope_108(play.var[i].name, script)
625 && strstr (*line_p, play.var[i].name)
626 && (prevar == NULL || (prevar != NULL && strstr (play.var[i].name, prevar))))
627 {
628 //Look for shorter variables
629 var_replace_108(i + 1, script, line_p, play.var[i].name);
630 //we didn't find any, so we replace!
631 char crap[20];
632 sprintf(crap, "%d", play.var[i].var);
633 replace(play.var[i].name, crap, line_p);
634 }
635 i++;
636 }
637 }
638
639 /**
640 * Replace all variables (&something) in 'line', with scope 'scope'
641 */
var_replace(char ** line_p,int scope)642 void var_replace(char** line_p, int scope)
643 {
644 if (dversion >= 108)
645 var_replace_108(1, scope, line_p, NULL);
646 else
647 var_replace_107(line_p, scope);
648 }
649
650
651 /**
652 * Similar to decipher, plus:
653 * - expand special choice variables &savegameinfo and &buttoninfo
654 * - it can replace several variables in the same string
655 * - with v1.07 it has a prefix bug (see var_replace_107)
656 */
decipher_string(char ** line_p,int script)657 void decipher_string(char** line_p, int script)
658 {
659 char buffer[20 + 1];
660
661 /* Replace all valid variables in 'line' */
662 var_replace(line_p, script);
663
664 if ((strchr(*line_p, '&') != NULL) && (script != 0))
665 {
666 sprintf(buffer, "%d", rinfo[script]->sprite); replace("¤t_sprite", buffer, line_p);
667 sprintf(buffer, "%d", script); replace("¤t_script", buffer, line_p);
668
669 if (dversion >= 108)
670 {
671 //v1.08 special variables.
672 sprintf(buffer, "%d", returnint); replace("&return", buffer, line_p);
673 sprintf(buffer, "%d", rinfo[script]->arg1); replace("&arg1", buffer, line_p);
674 sprintf(buffer, "%d", rinfo[script]->arg2); replace("&arg2", buffer, line_p);
675 sprintf(buffer, "%d", rinfo[script]->arg3); replace("&arg3", buffer, line_p);
676 sprintf(buffer, "%d", rinfo[script]->arg4); replace("&arg4", buffer, line_p);
677 sprintf(buffer, "%d", rinfo[script]->arg5); replace("&arg5", buffer, line_p);
678 sprintf(buffer, "%d", rinfo[script]->arg6); replace("&arg6", buffer, line_p);
679 sprintf(buffer, "%d", rinfo[script]->arg7); replace("&arg7", buffer, line_p);
680 sprintf(buffer, "%d", rinfo[script]->arg8); replace("&arg8", buffer, line_p);
681 sprintf(buffer, "%d", rinfo[script]->arg9); replace("&arg9", buffer, line_p);
682 }
683
684 if (decipher_savegame != 0)
685 {
686 int button_action = input_get_button_action(decipher_savegame-1);
687 if (button_action == ACTION_ATTACK) replace("&buttoninfo", _("Attack"), line_p);
688 else if (button_action == ACTION_TALK) replace("&buttoninfo", _("Talk/Examine"), line_p);
689 else if (button_action == ACTION_MAGIC) replace("&buttoninfo", _("Magic"), line_p);
690 else if (button_action == ACTION_INVENTORY) replace("&buttoninfo", _("Item Screen"), line_p);
691 else if (button_action == ACTION_MENU) replace("&buttoninfo", _("Main Menu"), line_p);
692 else if (button_action == ACTION_MAP) replace("&buttoninfo", _("Map"), line_p);
693 else if (button_action == ACTION_BUTTON7) replace("&buttoninfo", _("Unused"), line_p);
694 else if (button_action == ACTION_BUTTON8) replace("&buttoninfo", _("Unused"), line_p);
695 else if (button_action == ACTION_BUTTON9) replace("&buttoninfo", _("Unused"), line_p);
696 else if (button_action == ACTION_BUTTON10) replace("&buttoninfo", _("Unused"), line_p);
697 else if (button_action == ACTION_DOWN) replace("&buttoninfo", _("Down"), line_p);
698 else if (button_action == ACTION_LEFT) replace("&buttoninfo", _("Left"), line_p);
699 else if (button_action == ACTION_RIGHT) replace("&buttoninfo", _("Right"), line_p);
700 else if (button_action == ACTION_UP) replace("&buttoninfo", _("Up"), line_p);
701 else replace("&buttoninfo", _("Error: not mapped"), line_p);
702 }
703 }
704
705 if ((decipher_savegame != 0) && compare(*line_p, "&savegameinfo"))
706 {
707 char gameinfo[196] = "";
708 int mytime = 0;
709
710 free(*line_p);
711 if (load_game_small(decipher_savegame, gameinfo, &mytime) == 1)
712 asprintf(line_p, _("Slot %d - %d:%02d - %s"), decipher_savegame,
713 mytime/60, mytime%60, gameinfo);
714 else
715 asprintf(line_p, _("Slot %d - Empty"), decipher_savegame);
716 }
717 }
718
719 /**
720 *
721 * name: name of the procedure() to call
722 * n1: wait at least n1 milliseconds before callback
723 * n2: wait at most n1+n2 milliseconds before callback
724 * script: number of the script currently running
725 **/
add_callback(char name[20],int n1,int n2,int script)726 int add_callback(char name[20], int n1, int n2, int script)
727 {
728 int k;
729 for (k = 1; k < MAX_CALLBACKS; k++)
730 {
731 if (callback[k].active == /*false*/0)
732 {
733 memset(&callback[k], 0, sizeof(callback[k]));
734
735 callback[k].active = /*true*/1;
736 callback[k].min = n1;
737 callback[k].max = n2;
738 callback[k].owner = script;
739 strcpy(callback[k].name, name);
740
741 log_debug("Callback added to %d.", k);
742 return(k);
743 }
744 }
745
746 log_error("Couldn't add callback, all out of space");
747 return 0;
748 }
749
kill_callback(int cb)750 void kill_callback(int cb)
751 {
752 if (cb >= 0 && cb <= 99)
753 callback[cb].active = /*false*/0;
754 }
755
kill_callbacks_owned_by_script(int script)756 void kill_callbacks_owned_by_script(int script)
757 {
758 int i = 1;
759 for (; i < MAX_CALLBACKS; i++)
760 {
761 if (callback[i].owner == script)
762 {
763 log_debug("Kill_all_callbacks just killed %d for script %d", i, script);
764 //killed callback
765 callback[i].active = /*false*/0;
766 }
767 }
768 }
769
770
kill_script(int k)771 void kill_script(int k)
772 {
773 if (rinfo[k] != NULL)
774 {
775 int i;
776
777 kill_callbacks_owned_by_script(k);
778
779 // Now let's kill all local vars associated with this script
780 for (i = 1; i < MAX_VARS; i++)
781 {
782 if (play.var[i].active && play.var[i].scope == k)
783 play.var[i].active = /*false*/0;
784 }
785 log_debug("Killed script %s. (num %d)", rinfo[k]->name, k);
786
787 if (rinfo[k]->name != NULL)
788 free(rinfo[k]->name);
789 if (rinfo[k] != NULL)
790 free(rinfo[k]);
791 rinfo[k] = NULL;
792 if (rbuf[k] != NULL)
793 free(rbuf[k]);
794 rbuf[k] = NULL;
795 }
796 }
797
798
799 /**
800 * Kill all scripts except those attached to pseudo-sprite 1000, which
801 * is meant to survive across screen changes
802 * (kill_all_scripts_for_real(...) is more brutal)
803 *
804 * Used by gfx_tiles only
805 */
kill_all_scripts(void)806 void kill_all_scripts(void)
807 {
808 /* Kill scripts (except if attached to pseudo-sprite 1000) */
809 int k = 1;
810 for (; k < MAX_SCRIPTS; k++)
811 {
812 if (rinfo[k] != NULL)
813 if (rinfo[k]->sprite != 1000)
814 kill_script(k);
815 }
816
817 /* Kill pending callbacks (except if attached to pseudo-sprite 1000) */
818 for (k = 1; k < MAX_CALLBACKS; k++)
819 {
820 if (callback[k].active
821 && (!(rinfo[callback[k].owner] != NULL)
822 && (rinfo[callback[k].owner]->sprite == 1000)))
823 {
824 log_debug("Killed callback %d. (was attached to script %d)",
825 k, callback[k].owner);
826 callback[k].active = 0;
827 }
828 }
829 }
830
831 /**
832 * Kill all scripts including those attached to pseudo-sprite 1000
833 */
kill_all_scripts_for_real(void)834 void kill_all_scripts_for_real(void)
835 {
836 int k = 1;
837 for (k = 1; k < MAX_SCRIPTS; k++)
838 {
839 if (rinfo[k] != NULL)
840 kill_script(k);
841 }
842
843 for (k = 1; k <= MAX_CALLBACKS; k++)
844 {
845 callback[k].active = 0;
846 }
847 }
848
849
850 /**
851 * Return the next single line from rbuf[script], starting at
852 * rinfo[script]->current. Update line/column counters.
853 */
read_next_line(int script)854 char* read_next_line(int script)
855 {
856 if (rinfo[script] == NULL || rbuf == NULL)
857 {
858 log_error("Tried to read script %d, it doesn't exist.", script);
859 return NULL;
860 }
861
862 if (rinfo[script]->current >= rinfo[script]->end)
863 {
864 //at end of buffer
865 return NULL;
866 }
867
868 /* remember the beginning of the line to be parsed, we'll use it in
869 the debugging messages */
870 rinfo[script]->debug_line = rinfo[script]->cur_line;
871
872 int k = rinfo[script]->current;
873 int start = k;
874 for (; k < rinfo[script]->end; k++)
875 {
876 rinfo[script]->current++;
877 rinfo[script]->cur_col++;
878
879 if (rbuf[script][k] == '\n')
880 {
881 rinfo[script]->cur_line++;
882 rinfo[script]->cur_col = 0;
883 }
884 if (rbuf[script][k] == '\n' || rbuf[script][k] == '\r')
885 break;
886 }
887
888 if (k < rinfo[script]->end)
889 {
890 int len = rinfo[script]->current - start;
891 char* buf = xmalloc(len + 1);
892
893 char* pc = buf;
894 int k = start;
895 for (; k < rinfo[script]->current; k++, pc++)
896 {
897 *pc = rbuf[script][k];
898
899 /* Compatibility substitutions, important when parsing
900 title_start/title_end, namely */
901 if (*pc == '\t') *pc = ' ';
902 if (*pc == '\r') *pc = '\n';
903 }
904 *pc = '\0'; /* for safety */
905 return buf;
906 }
907 else
908 {
909 //at end of buffer
910 return NULL;
911 }
912 }
913
914 /**
915 * Run callbacks, order by index. Sets the activation delay if
916 * necessary. Kill obsolete callbacks along the way.
917 *
918 * Callbacks are set by wait() and set_callback_random().
919 *
920 * spawn()/external()/etc. use other mechanisms. say_stop*() also use
921 * callbacks, but implemented differently (spr[x].callback, processed
922 * in updateFrame()).
923 **/
process_callbacks(void)924 void process_callbacks(void)
925 {
926 int now = game_GetTicks();
927 int i, k;
928
929 for (i = 1; i < MAX_SCRIPTS; i++)
930 {
931 if (rinfo[i] != NULL)
932 {
933 if (rinfo[i]->sprite > 0 && rinfo[i]->sprite != 1000 && spr[rinfo[i]->sprite].active == /*false*/0)
934 {
935 //kill this script, owner is dead
936 log_debug("Killing script %s, owner sprite %d is dead.", rinfo[i]->name, rinfo[i]->sprite);
937 kill_script(i);
938 }
939 }
940 }
941
942 for (k = 1; k < MAX_CALLBACKS; k++)
943 {
944 if (callback[k].active)
945 {
946 if (callback[k].owner > 0 && rinfo[callback[k].owner] == NULL)
947 {
948 //kill this process, it's owner sprite is 'effin dead.
949 log_debug("Killed callback %s because script %d is dead.",
950 k, callback[k].owner);
951 callback[k].active = /*false*/0;
952 }
953 else
954 {
955 if (callback[k].timer == 0)
956 {
957 //set timer
958
959 if (callback[k].max > 0)
960 callback[k].timer = now + (rand() % callback[k].max) + callback[k].min;
961 else
962 callback[k].timer = now + callback[k].min;
963 }
964 else
965 {
966 if (callback[k].timer < now)
967 {
968 callback[k].timer = 0;
969
970 if (compare(callback[k].name, ""))
971 {
972 //callback defined no proc name, so lets assume they want to start the script where it
973 //left off
974 //kill this callback
975 callback[k].active = /*false*/0;
976 run_script(callback[k].owner);
977 log_debug("Called script %d from callback %d.",
978 callback[k].owner, k);
979 }
980 else
981 {
982 log_debug("Called proc %s from callback %d.", callback[k].name, k);
983
984 //callback defined a proc name
985 if (locate(callback[k].owner,callback[k].name))
986 {
987 //found proc, lets run it
988 run_script(callback[k].owner);
989 }
990 }
991 }
992 }
993 }
994 }
995 }
996 }
997
998
999 /**
1000 * Run main() for all active sprites on screen
1001 */
init_scripts()1002 void init_scripts()
1003 {
1004 int k = 1;
1005 for (; k < MAX_SCRIPTS; k++)
1006 {
1007 if (rinfo[k] != NULL && rinfo[k]->sprite != 0
1008 /* don't go out of bounds in spr[300], e.g. when sprite == 1000: */
1009 && rinfo[k]->sprite < MAX_SPRITES_AT_ONCE
1010 && spr[rinfo[k]->sprite].active)
1011 {
1012 if (locate(k, "main"))
1013 {
1014 log_debug("Screendraw: running main of script %s..", rinfo[k]->name);
1015 run_script(k);
1016 }
1017 }
1018 }
1019 }
1020
1021
1022
kill_scripts_owned_by(int sprite)1023 void kill_scripts_owned_by(int sprite)
1024 {
1025 int i;
1026 for (i = 1; i < MAX_SCRIPTS; i++)
1027 {
1028 if (rinfo[i] != NULL)
1029 {
1030 if (rinfo[i]->sprite == sprite)
1031 {
1032 kill_script(i);
1033
1034 }
1035
1036 }
1037 }
1038
1039 }
1040
kill_returning_stuff(int script)1041 void kill_returning_stuff(int script)
1042 {
1043 //Msg("Checking callbacks..");
1044 //check callbacks
1045
1046 int i;
1047 // callbacks from wait() and run_script_by_number()
1048 for (i = 1; i < MAX_CALLBACKS; i++)
1049 {
1050 if (callback[i].active && callback[i].owner == script)
1051 // if (compare(callback[i].name, ""))
1052 {
1053 log_debug("killed a returning callback, ha!");
1054 callback[i].active = /*false*/0;
1055 }
1056
1057 }
1058
1059 // callbacks from say_*()
1060 for (i = 1; i <= last_sprite_created; i++)
1061 {
1062 if (spr[i].active && spr[i].brain == 8 && spr[i].callback == script)
1063 {
1064 log_debug("Killed sprites callback command");
1065 spr[i].callback = 0;
1066 }
1067 }
1068 }
1069
1070
run_script(int script)1071 void run_script(int script)
1072 {
1073 int result;
1074 char* line = NULL;
1075
1076 /* keep 'return' value? */
1077 if (dversion >= 108)
1078 {
1079 if (bKeepReturnInt == 1)
1080 {
1081 bKeepReturnInt = 0;
1082 }
1083 else
1084 {
1085 returnint = 0;
1086 }
1087 }
1088 else
1089 {
1090 returnint = 0;
1091 }
1092 returnstring[0] = 0;
1093
1094
1095 if (rinfo[script] != NULL)
1096 {
1097 log_debug("Script %s is entered at %d:%d (offset %d).",
1098 rinfo[script]->name,
1099 rinfo[script]->cur_line, rinfo[script]->cur_col,
1100 rinfo[script]->current);
1101 }
1102 else
1103 {
1104 log_error("Tried to run a script that doesn't exist in memory. Nice work.");
1105 }
1106
1107 int doelse_once = 0;
1108 while ((line = read_next_line(script)) != NULL)
1109 {
1110 while (1)
1111 {
1112 strip_beginning_spaces(line);
1113 if (strcmp(line, "\n") == 0)
1114 break;
1115
1116
1117 int doelse = 0;
1118 if (doelse_once == 1)
1119 {
1120 doelse = 1;
1121 doelse_once = 0;
1122 }
1123 result = process_line(script, line, doelse);
1124
1125
1126 if (result == DCPS_DOELSE_ONCE)
1127 {
1128 doelse_once = 1;
1129 /* now process the rest of the line */
1130 }
1131
1132 if (result == DCPS_YIELD)
1133 {
1134 /* Quit script: */
1135 log_debug("giving script the boot");
1136 free(line);
1137 return;
1138 }
1139
1140 if (result == DCPS_GOTO_NEXTLINE)
1141 break;
1142
1143 /* else result == DCPS_CONTINUE */
1144 }
1145 free(line);
1146 }
1147
1148 if (rinfo[script] != NULL && rinfo[script]->proc_return != 0)
1149 {
1150 run_script(rinfo[script]->proc_return);
1151 kill_script(script);
1152 }
1153 }
1154
var_exists(char name[20],int scope)1155 int var_exists(char name[20], int scope)
1156 {
1157 int i;
1158 for (i = 1; i < MAX_VARS; i++)
1159 {
1160 if (play.var[i].active)
1161 {
1162 if (compare(play.var[i].name, name))
1163 {
1164
1165 if (scope == play.var[i].scope)
1166 {
1167 //Msg("Found match for %s.", name);
1168 return(i);
1169 }
1170 }
1171
1172
1173
1174 }
1175 }
1176
1177 return(0);
1178 }
1179
1180 /**
1181 * Make new global functions (v1.08)
1182 */
make_function(char file[10],char func[20])1183 void make_function (char file[10], char func[20])
1184 {
1185 //See if it already exists
1186
1187 int exists = 0;
1188 int i;
1189 for (i = 0; strlen (play.func[i].func) > 0 && i < 100; i++)
1190 {
1191 if (compare (func, play.func[i].func))
1192 {
1193 exists = 1;
1194 break;
1195 }
1196 }
1197 if (exists == 1)
1198 {
1199 strncpy (play.func[i].file, file, 10);
1200 }
1201 else
1202 {
1203 strncpy (play.func[0].file, file, 10);
1204 strncpy (play.func[0].func, func, 20);
1205 }
1206 }
1207
1208
make_int(char name[80],int value,int scope,int script)1209 void make_int(char name[80], int value, int scope, int script)
1210 {
1211 int dupe;
1212 int i;
1213 if (strlen(name) > 19)
1214 {
1215
1216 log_error("[DinkC] %s:%d: varname %s is too long",
1217 rinfo[script]->name, rinfo[script]->debug_line, name);
1218 return;
1219 }
1220 dupe = var_exists(name, scope);
1221
1222 if (dupe > 0)
1223 {
1224 if (scope != DINKC_GLOBAL_SCOPE)
1225 {
1226 log_warn("[DinkC] %s:%d: Local var %s already used in this procedure",
1227 rinfo[script]->name, rinfo[script]->debug_line,
1228 name, rinfo[script]->name);
1229
1230 play.var[dupe].var = value;
1231 }
1232 else
1233 {
1234 log_warn("[DinkC] %s:%d: var %s is already a global, not changing value",
1235 rinfo[script]->name, rinfo[script]->debug_line, name);
1236 }
1237 return;
1238 }
1239
1240
1241 //make new var
1242
1243 for (i = 1; i < MAX_VARS; i++)
1244 {
1245 if (play.var[i].active == /*false*/0)
1246 {
1247
1248 play.var[i].active = /*true*/1;
1249 play.var[i].scope = scope;
1250 strcpy(play.var[i].name, name);
1251 //g("var %s created, used slot %d ", name,i);
1252 play.var[i].var = value;
1253 return;
1254 }
1255 }
1256
1257 log_error("[DinkC] %s:%d: out of var space, all %d used",
1258 rinfo[script]->name, rinfo[script]->debug_line, MAX_VARS);
1259 }
1260
1261 /**
1262 * (re)Define variable
1263 *
1264 * name: variable name
1265 * newname: new value (unless that's a function call, cf. 'rest')
1266 * math: operator (one of '=', '+', '-', '*', '/')
1267 * script: in-memory script identifier
1268 * rest: text of the script after the operator (left-trimmed)
1269 */
var_equals(char * name,char * newname,char math,int script,char rest[200])1270 void var_equals(char* name, char* newname, char math, int script, char rest[200])
1271 {
1272 int newval = 0;
1273 struct varman *lhs_var = NULL;
1274
1275 /** Ensure left-hand side is an existing variable **/
1276 if (name[0] != '&')
1277 {
1278 log_error("[DinkC] %s:%d:[var_equals]: unknown var %s",
1279 rinfo[script]->name, rinfo[script]->debug_line, name);
1280 return;
1281 }
1282 /* Find the variable slot */
1283 {
1284 int k = search_var_with_this_scope(name, script);
1285 if (k != -1)
1286 lhs_var = &(play.var[k]);
1287
1288 if (lhs_var == NULL) /* not found */
1289 {
1290 log_error("[DinkC] %s:%d:[var_equals]: unknown var %s",
1291 rinfo[script]->name, rinfo[script]->debug_line, name);
1292 return;
1293 }
1294 }
1295
1296 /** Analyse right-hand side **/
1297 /* check if right-hand side is a function */
1298 if (strchr(rest, '(') != NULL)
1299 {
1300 process_line(script, rest, /*false*/0);
1301 newval = returnint;
1302 goto next2;
1303 }
1304
1305 /* check if right-hand side is a variable to copy */
1306 /* remove trailing ';' */
1307 if (strchr(newname, ';') != NULL)
1308 replace_norealloc(";", "", newname);
1309 /* look for existing variable */
1310 {
1311 int k2 = search_var_with_this_scope(newname, script);
1312 if (k2 != -1)
1313 {
1314 newval = play.var[k2].var;
1315 //found var
1316 goto next2;
1317 }
1318 }
1319 /* also check special variables */
1320 if (compare(newname, "¤t_sprite"))
1321 {
1322 newval = rinfo[script]->sprite;
1323 goto next2;
1324 }
1325 if (compare(newname, "¤t_script"))
1326 {
1327 newval = script;
1328 goto next2;
1329 }
1330 if (dversion >= 108)
1331 {
1332 //v1.08 special variables.
1333 if (compare (newname, "&return"))
1334 {
1335 newval = returnint;
1336 goto next2;
1337 }
1338 if (compare (newname, "&arg1"))
1339 {
1340 newval = rinfo[script]->arg1;
1341 goto next2;
1342 }
1343 if (compare (newname, "&arg2"))
1344 {
1345 newval = rinfo[script]->arg2;
1346 goto next2;
1347 }
1348 if (compare (newname, "&arg3"))
1349 {
1350 newval = rinfo[script]->arg3;
1351 goto next2;
1352 }
1353 if (compare (newname, "&arg4"))
1354 {
1355 newval = rinfo[script]->arg4;
1356 goto next2;
1357 }
1358 if (compare (newname, "&arg5"))
1359 {
1360 newval = rinfo[script]->arg5;
1361 goto next2;
1362 }
1363 if (compare (newname, "&arg6"))
1364 {
1365 newval = rinfo[script]->arg6;
1366 goto next2;
1367 }
1368 if (compare (newname, "&arg7"))
1369 {
1370 newval = rinfo[script]->arg7;
1371 goto next2;
1372 }
1373 if (compare (newname, "&arg8"))
1374 {
1375 newval = rinfo[script]->arg8;
1376 goto next2;
1377 }
1378 if (compare (newname, "&arg9"))
1379 {
1380 newval = rinfo[script]->arg9;
1381 goto next2;
1382 }
1383 }
1384 /* otherwise, assume right-hand side is an integer */
1385 newval = atol(newname);
1386
1387
1388 next2:
1389 /* Apply the right operation */
1390 if (math == '=')
1391 lhs_var->var = newval;
1392 if (math == '+')
1393 lhs_var->var += newval;
1394 if (math == '-')
1395 lhs_var->var -= newval;
1396 if (math == '/')
1397 lhs_var->var = lhs_var->var / newval;
1398 if (math == '*')
1399 lhs_var->var = lhs_var->var * newval;
1400 }
1401
1402 /**
1403 * Evaluate a value (variable, int, or maths), in the context of
1404 * 'script'.
1405 */
var_figure(char * h,int script)1406 int var_figure(char* h, int script)
1407 {
1408 char* word = NULL;
1409 int ret = 0;
1410 int n1 = 0, n2 = 0;
1411
1412 int is_one_word_equation = 0;
1413 word = get_word(h, 2);
1414 if (compare(word, ""))
1415 is_one_word_equation = 1;
1416 free(word);
1417 if (is_one_word_equation)
1418 {
1419 // variable -> integer
1420 if (h[0] == '&')
1421 decipher_string(&h, script);
1422
1423 // integer
1424 ret = atol(h);
1425 return ret;
1426 }
1427
1428 word = get_word(h, 1);
1429 decipher_string(&word, script);
1430 n1 = atol(word);
1431 free(word);
1432
1433 word = get_word(h, 3);
1434 replace_norealloc(")", "", word);
1435 decipher_string(&word, script);
1436 n2 = atol(word);
1437 free(word);
1438
1439 word = get_word(h, 2);
1440 log_debug("Compared %d to %d", n1, n2);
1441
1442 if (compare(word, "=="))
1443 {
1444 if (n1 == n2) ret = 1; else ret = 0;
1445 free(word);
1446 return ret;
1447 }
1448
1449 if (compare(word, ">"))
1450 {
1451 if (n1 > n2) ret = 1; else ret = 0;
1452 free(word);
1453 return ret;
1454 }
1455
1456 if (compare(word, ">="))
1457 {
1458 if (n1 >= n2) ret = 1; else ret = 0;
1459 free(word);
1460 return ret;
1461 }
1462
1463
1464 if (compare(word, "<"))
1465 {
1466 if (n1 < n2) ret = 1; else ret = 0;
1467 free(word);
1468 return ret;
1469 }
1470
1471 if (compare(word, "<="))
1472 {
1473 if (n1 <= n2) ret = 1; else ret = 0;
1474 free(word);
1475 return ret;
1476 }
1477
1478 if (compare(word, "!="))
1479 {
1480 if (n1 != n2) ret = 1; else ret = 0;
1481 free(word);
1482 return ret;
1483 }
1484
1485 free(word);
1486 return ret;
1487 }
1488
1489 /**
1490 * Check if 'line' is a valid variable declaration, and define the
1491 * variable it to 0 (via make_int(...)). 'line' is modified.
1492 */
int_prepare(char * line,int script)1493 void int_prepare(char* line, int script)
1494 {
1495 char* hold = strdup(line);
1496
1497 char* name = NULL;
1498 char *temp = NULL;
1499 replace_norealloc("=", " ", line);
1500 temp = separate_string(line, 1, ';');
1501 strcpy(line, temp); // safe as strlen(line) <= strlen(temp)
1502 free(temp);
1503 name = get_word(line, 2);
1504
1505 if (name[0] != '&')
1506 {
1507 log_error("[DinkC] %s:%d: can't create var %s, should be &%s.",
1508 rinfo[script]->name, rinfo[script]->debug_line,
1509 name, name);
1510 }
1511 else
1512 {
1513 make_int(name, 0, script, script);
1514
1515 strcpy(line, hold);
1516 }
1517 free(name);
1518 free(hold);
1519 }
1520
1521
dinkc_init()1522 void dinkc_init()
1523 {
1524 dinkc_bindings_init();
1525 }
1526
dinkc_quit()1527 void dinkc_quit()
1528 {
1529 dinkc_bindings_quit();
1530 }
1531