home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / x / xconq55.zip / xc5.5 / period.c < prev    next >
C/C++ Source or Header  |  1991-08-23  |  32KB  |  1,044 lines

  1. /* Copyright (c) 1987, 1988, 1991  Stanley T. Shebs. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. /* This file defines a mini-postfix language for use in specifying periods. */
  6. /* The language is very restrictive, both lexically and semantically.  The */
  7. /* only kinds of objects are integers, strings, symbols, and vectors, while */
  8. /* the only way to get new words is to make new unit, resource, and terrain */
  9. /* types, or to create names for constants.  There is a strong flavor of APL */
  10. /* in the kinds of vector operations available. */
  11.  
  12. /* Stack hacking is a little dubious - we pop the stack *before* doing the */
  13. /* word, but don't copy args.  Potential for disaster if word pushes */
  14. /* results too soon... Should use pointers to objects also, for portability. */
  15.  
  16. #include "config.h"
  17. #include "misc.h"
  18. #include "period.h"
  19. #include "side.h"
  20. #include "unit.h"
  21.  
  22. /* Cute little macro from the MG people.  It computes an offset of the slot */
  23. /* "m" from the beginning of structure type "t". */
  24.  
  25. #define OFFSET(t,m) ((char *) &(((t *) 0)->m) - ((char *) ((t *) 0)))
  26. #define VOFFSET(t,m) ((char *) (((t *) 0)->m) - ((char *) ((t *) 0)))
  27.  
  28. /* The "dictionary" is a symbol table, thus needs to be large enough to hold */
  29. /* all pre-defined symbols, as well as those added during execution. */
  30. /* There are about 200 predefined words. */
  31.  
  32. #define DICTSIZE 400
  33.  
  34. /* The stack doesn't have to be very deep, unless the period description is */
  35. /* really perverted.  Depth should be at least MAXUTYPES+5 or so... */
  36.  
  37. #define STACKSIZE 50
  38.  
  39. /* An object has four possible types.  Numbers can only be integers, and */
  40. /* vectors may only contain integers.  Strings are unlimited, while symbols */
  41. /* mustn't overflow the dictionary. */
  42.  
  43. #define INT 0
  44. #define SYM 1
  45. #define STR 2
  46. #define VEC 3
  47.  
  48. /* States of tokenizer (along with symbols just above). */
  49.  
  50. #define ENDTOKEN (-99)
  51. #define NIL (-1)
  52.  
  53. /* Types of words (avoids using C hook functions usually). */
  54.  
  55. #define FN 0            /* arbitrary function */
  56. #define U1 1                    /* put number into utype slot */
  57. #define S1 2                    /* put string into utype slot */
  58. #define U2 3                    /* put number(s) into utype array slot */
  59. #define VV 5                    /* push value onto stack */
  60. #define P0 6                    /* put number into period structure */
  61. #define T1 7                    /* put number into terrain type slot */
  62.  
  63. /* Note that this declaration results in largish objects, but anything more */
  64. /* clever is probably overkill.  */
  65.  
  66. /* MAXUTYPES must be larger than MAX[RT]TYPES (obscure). */
  67.  
  68. typedef struct a_obj {
  69.     int type;          /* type of the object */
  70.     int len;           /* length if it's a vector */
  71.     union {
  72.     int num;       /* numeric value */
  73.     char *str;     /* string value */
  74.     int sym;       /* symbol value (index into dictionary) */
  75.     short vec[MAXUTYPES];  /* vector value */
  76.     } v;               /* value is pretty sizeable because of vector values */
  77. } Obj;
  78.  
  79. /* The dictionary interfaces words to C functions. */
  80.  
  81. typedef struct dict {
  82.     char *name;        /* a word of the language */
  83.     int wtype;         /* type of word (1-d set, string set, etc) */
  84.     int nargs;         /* how much to pull from the stack */
  85.     union {
  86.         int (*code)(); /* pointer to a C function */
  87.         int offset;    /* for offsets into structures */
  88.         Obj value;     /* a "default" value used sometimes */
  89.     } f;
  90. } Dict;
  91.  
  92. Dict words[DICTSIZE];  /* the dictionary itself */
  93.  
  94. Obj stack[STACKSIZE];  /* the evaluation stack */
  95. Obj curobj;           /* the current word (used in weird way) */
  96.  
  97. FILE *pfp;             /* period file pointer */
  98.  
  99. bool mustbenew = TRUE;  /* flag for an efficiency hack during init */
  100.  
  101. int numwords;          /* number of words presently in dictionary */
  102. int top = 0;           /* index to the top of the stack */
  103.  
  104. int push_value();      /* predeclare to cover some forward refs */
  105.  
  106. /* Read a period section of an already-opened mapfile. */
  107. /* The debug prints are very important for debugging period files - they */
  108. /* provide at least a simple test that things are being read correctly. */
  109. /* There are also a number of consistency checks, though not all possible, */
  110. /* and nothing is done to test play balance for the period :-). */
  111.  
  112. read_period(fp)
  113. FILE *fp;
  114. {
  115.     int extension, endsym, si;
  116.  
  117.     pfp = fp;
  118.     fscanf(pfp, "Period %d\n", &extension);
  119.     if (Debug) printf("Trying to read period ...\n");
  120.     init_words();
  121.     endsym = lookup_symbol("end");
  122.     if (extension == 0) {
  123.     clear_period();
  124.     }
  125.     while (get_token(pfp)) {
  126.     if (Debug) {
  127.         print_object(curobj, FALSE);
  128.     }
  129.     if (curobj.type == SYM) {
  130.         si = curobj.v.sym;
  131.         if (si == endsym) break;
  132.         if (top < words[si].nargs) {
  133.         fprintf(stderr, "Stack underflow!\n");
  134.         exit(1);
  135.         }
  136.         top -= words[si].nargs;
  137.         switch (words[si].wtype) {
  138.         case FN:
  139.         execute_word(words[si].f.code, words[si].nargs);
  140.         break;
  141.         case P0:
  142.         set_0d_n(stack[top], words[si].f.offset);
  143.         break;
  144.         case U1:
  145.         set_1d_n(stack[top], stack[top+1], words[si].f.offset);
  146.         break;
  147.         case S1:
  148.         set_1d_s(stack[top], stack[top+1], words[si].f.offset);
  149.         break;
  150.         case U2:
  151.         set_2d(stack[top], stack[top+1], stack[top+2],
  152.                words[si].f.offset);
  153.         break;
  154.         case VV:
  155.         push_value();
  156.         break;
  157.         case T1:
  158.         set_1d_t(stack[top], stack[top+1], words[si].f.offset);
  159.         break;
  160.         default:
  161.             case_panic("word type", words[si].wtype);
  162.         break;
  163.         }
  164.     } else {
  165.         push_word();
  166.     }
  167.     }
  168.     check_period_numbers();
  169.     if (Debug) printf("... Done reading period.\n");
  170. }
  171.  
  172. /* Execution is straightforward, but we need to know how many arguments each */
  173. /* C function expects. */
  174.  
  175. execute_word(fn, args)
  176. int (*fn)(), args;
  177. {
  178.     if (fn == NULL) abort();
  179.     switch (args) {
  180.     case 0:
  181.     (*fn)();
  182.     break;
  183.     case 1:
  184.     (*fn)(stack[top]);
  185.     break;
  186.     case 2:
  187.     (*fn)(stack[top], stack[top+1]);
  188.     break;
  189.     case 3:
  190.     (*fn)(stack[top], stack[top+1], stack[top+2]);
  191.     break;
  192.     case 4:
  193.     (*fn)(stack[top], stack[top+1], stack[top+2], stack[top+3]);
  194.     break;
  195.     default:
  196.         case_panic("argument", args);
  197.     break;
  198.     }
  199. }
  200.  
  201. /* Alas, the tokenizer is too simple to bother with lex, and does too much */
  202. /* to be a neat and short bit of code.  The states are NIL, INT, STR, SYM, */
  203. /* and ENDTOKEN.  The process starts in NIL, and succeeds in the state */
  204. /* ENDTOKEN, but fails (returning flag) if there is any sort of error. */
  205. /* Basically each token type is flagged by its special character(s), and */
  206. /* is terminated by whitespace (i.e. state machine is a bunch of looping */
  207. /* states with only other transitions to and from NIL). */
  208.  
  209. get_token(fp)
  210. FILE *fp;
  211. {
  212.     bool minus;
  213.     char ch, str[BUFSIZE];
  214.     int state = NIL, j = 0;
  215.  
  216.     while (state != ENDTOKEN) {
  217.     ch = getc(fp);
  218.     switch (ch) {
  219.     case EOF:
  220.         return FALSE;
  221.     case ';':
  222.         while ((ch = getc(fp)) != '\n' && ch != EOF);
  223.         break;
  224.     default:
  225.         switch (state) {
  226.         case NIL:
  227.         if (isdigit(ch) || ch == '-') {
  228.             minus = (ch == '-');
  229.             curobj.v.num = (minus ? 0 : ch - '0');
  230.             state = curobj.type = INT;
  231.         } else if (iswhite(ch)) {
  232.             /* nothing to do here */
  233.         } else if (ch == '"') {
  234.             state = curobj.type = STR;
  235.         } else {
  236.             str[j++] = ch;
  237.             state = curobj.type = SYM;
  238.         }
  239.         break;
  240.         case INT:
  241.         if (isdigit(ch)) {
  242.             curobj.v.num = curobj.v.num * 10 + ch - '0';
  243.         } else {
  244.             if (minus) curobj.v.num = 0 - curobj.v.num;
  245.             state = ENDTOKEN;
  246.         }
  247.         break;
  248.         case STR:
  249.         if (ch == '"') {
  250.             str[j] = '\0';
  251.             curobj.v.str = copy_string(str);
  252.             state = ENDTOKEN;
  253.         } else {
  254.             str[j++] = ch;
  255.         }
  256.         break;
  257.         case SYM:
  258.         if (iswhite(ch)) {
  259.             str[j] = '\0';
  260.             curobj.v.sym = lookup_symbol(str);
  261.             if (curobj.v.sym < 0) {
  262.             fprintf(stderr,
  263.                 "Warning: undefined word \"%s\" in period\n",
  264.                 str);
  265.             /* Don't get bent out of shape, keep going */
  266.             curobj.type = INT;
  267.             curobj.v.num = 0;
  268.             /* Blast the stack because it might overflow */
  269.             top = 0;
  270.             }
  271.             state = ENDTOKEN;
  272.         } else {
  273.             str[j++] = ch;
  274.         }
  275.         break;
  276.         default:
  277.         case_panic("token state", state);
  278.         break;
  279.         }
  280.         break;
  281.     }
  282.     }
  283.     return TRUE;
  284. }
  285.  
  286. /* The dictionary starts out empty.  During initialization before reading, */
  287. /* the basic set of predefined words come in.  Then units and resources get */
  288. /* words later on as requested.  The "mustbenew" flag enables/disables redef */
  289. /* checks.  Return address of new word. as a convenience. */
  290.  
  291. add_word(word, type, args, fn)
  292. char *word;
  293. int type, args;
  294. int (*fn)();
  295. {
  296.     int sym;
  297.  
  298.     if (mustbenew && (sym = lookup_symbol(word)) > 0) {
  299.     fprintf(stderr, "May not redefine \"%s\"!\n", word);
  300.     return sym;
  301.     }
  302.     if (numwords < DICTSIZE) {
  303.     words[numwords].name = word;
  304.     words[numwords].wtype = type;
  305.     words[numwords].nargs = args;
  306.     words[numwords].f.code = fn;
  307.     numwords++;
  308.     return (numwords - 1);
  309.     } else {
  310.     fprintf(stderr, "Dictionary is full!\n");
  311.     exit(1);
  312.     }
  313. }
  314.  
  315. /* Look up a symbol in the (not so small) dictionary.  DO NOT add it if not */
  316. /* found - don't want any screwups to be compounded by feeble DWIM efforts. */
  317.  
  318. lookup_symbol(str)
  319. char *str;
  320. {
  321.     int i;
  322.  
  323.     for (i = 0; i < numwords; ++i) {
  324.     if (strcmp(words[i].name, str) == 0) return i;
  325.     }
  326.     return (-1);
  327. }
  328.  
  329. /* Put the current word on the stack, checking for overflow first. */
  330.  
  331. push_word()
  332. {
  333.     if (top < STACKSIZE) {
  334.     stack[top++] = curobj;
  335.     } else {
  336.     fprintf(stderr, "Stack overflow!\n");
  337.     exit(1);
  338.     }
  339. }
  340.  
  341. /* Push the "value" slot of the word being executed onto the stack. */
  342. /* Used by all names and characters of units and resources - necessary */
  343. /* because we can't define individual distinct C codes for each type. */
  344.  
  345. push_value()
  346. {
  347.     if (top < STACKSIZE) {
  348.     stack[top++] = words[curobj.v.sym].f.value;
  349.     } else {
  350.     print_stack();
  351.     fprintf(stderr, "Stack overflow!\n");
  352.     exit(1);
  353.     }
  354. }
  355.  
  356. /* Generic number pusher. */
  357.  
  358. push_number(n)
  359. int n;
  360. {
  361.     if (top < STACKSIZE) {
  362.     stack[top].type = INT;
  363.     stack[top].v.num = n;
  364.     ++top;
  365.     } else {
  366.     fprintf(stderr, "Stack overflow!\n");
  367.     exit(1);
  368.     }
  369. }
  370.  
  371. /* Fill in some very basic numbers at the outset. This is essentially a */
  372. /* "clear" operation, rampaging straight through the three period structures */
  373. /* then filling in a few numbers that *must* have reasonable values for the */
  374. /* program to work.  A couple are convenience values, such as "nukehit". */
  375.  
  376. clear_period()
  377. {
  378.     char *base;
  379.     int i, u, u2, r, t, tmp1, tmp2;
  380.  
  381.     tmp1 = period.numsnames;  tmp2 = period.numunames;
  382.     base = (char *) .
  383.     for (i = 0; i < sizeof(Period); ++i) base[i] = '\0';
  384.     base = (char *) utypes;
  385.     for (i = 0; i < sizeof(Utype) * MAXUTYPES; ++i) base[i] = '\0';
  386.     base = (char *) rtypes;
  387.     for (i = 0; i < sizeof(Rtype) * MAXRTYPES; ++i) base[i] = '\0';
  388.     base = (char *) ttypes;
  389.     for (i = 0; i < sizeof(Ttype) * MAXTTYPES; ++i) base[i] = '\0';
  390.     period.numsnames = tmp1;  period.numunames = tmp2;
  391.     period.name = "unspecified";
  392.     period.fontname = "";
  393.     period.nukehit = 50;
  394.     period.countrysize = 3;
  395.     period.mindistance = 7;
  396.     period.maxdistance = 60;
  397.     period.firstutype = period.firstptype = NOTHING;
  398.     period.counterattack = TRUE;
  399.     period.capturemoves = TRUE;
  400.     period.defaultterrain = period.edgeterrain = -1;
  401.     period.altroughness = 80;  period.wetroughness = 70;
  402.     period.machineadvantage = FALSE;
  403.     period.spychance = 1;  period.spyquality = 50;
  404.     period.leavemap = FALSE;
  405.     period.repairscale = 1;
  406.     for (u = 0; u < MAXUTYPES; ++u) {
  407.     utypes[u].attdamage = 1;
  408.     utypes[u].attritionmsg = "suffers attrition";
  409.     utypes[u].accidentmsg = "has an accident";
  410.     utypes[u].starvemsg = "runs out of supplies and dies";
  411.     utypes[u].destroymsg = "destroys";
  412.     utypes[u].visibility = 100;
  413.     utypes[u].seebest  = 100;
  414.     utypes[u].seeworst = 100;
  415.     utypes[u].seerange = 1;
  416.     utypes[u].hp = 1;
  417.     utypes[u].counterable = TRUE;
  418.     utypes[u].consume_as_occupant = TRUE;
  419.     for (u2 = 0; u2 < MAXUTYPES; ++u2) {
  420.         utypes[u].entertime[u2] = 1;
  421.         utypes[u].mobility[u2] = 100;
  422.     }
  423.     for (r = 0; r < MAXRTYPES; ++r) {
  424.         utypes[u].stockpile[r] = 100;
  425.     }
  426.     for (t = 0; t < MAXTTYPES; ++t) {
  427.         utypes[u].moves[t] = -1;
  428.     }
  429.     }
  430.     for (t = 0; t < MAXTTYPES; ++t) {
  431.     ttypes[t].minalt = ttypes[t].minwet = 0;
  432.     ttypes[t].maxalt = ttypes[t].maxwet = 100;
  433.     }
  434. }
  435.  
  436. /* Random side and unit names must be cleared separately by explicit cmd, */
  437. /* since periods don't always want to define their own. */
  438.  
  439. clear_side_names() {  period.numsnames = 0;  }
  440.  
  441. clear_unit_names() {  period.numunames = 0;  }
  442.  
  443. /* Words that set the contents of arrays are polymorphic in that they can */
  444. /* do array-like assignments as well as single value assignment. */
  445.  
  446. set_0d_n(n, offset)
  447. Obj n;
  448. int offset;
  449. {
  450.     *((short *) ((char *) ((char *) &period) + offset)) = n.v.num;
  451. }
  452.  
  453. /* No string vectors, so all we can do is to assign the same string to */
  454. /* one or more different array positions. */
  455.  
  456. #define s1ds(s,i) (*((char **)((char *)((char *)&(utypes[i])) + offset)) = s)
  457.  
  458. set_1d_s(s, i, offset)
  459. Obj s, i;
  460. int offset;
  461. {
  462.     int z;
  463.  
  464.     if (s.type == STR) {
  465.     if (i.type == INT) {
  466.         s1ds(s.v.str, i.v.num);
  467.     } else {
  468.         for (z = 0; z < i.len; ++z)    s1ds(s.v.str, i.v.vec[z]);
  469.     }
  470.     } else {
  471.     arg_error_1d("1st arg must be string", s, i);
  472.     }
  473. }
  474.  
  475. /* One-dimensional arrays allow the combinations of scalar value & index SS, */
  476. /* scalar value & vector index SV, and two vectors VV.  The 4th combo is not */
  477. /* sensible. */
  478.  
  479. #define s1d(n,i) (*((short *)((char *)((char *)&(utypes[i])) + offset)) = n)
  480.  
  481. set_1d_n(n, i, offset)
  482. Obj n, i;
  483. int offset;
  484. {
  485.     int z;
  486.  
  487.     if (n.type == INT) {
  488.     if (i.type == INT) {
  489.         s1d(n.v.num, i.v.num);
  490.     } else {
  491.         for (z = 0; z < i.len; ++z)    s1d(n.v.num, i.v.vec[z]);
  492.     }
  493.     } else {
  494.     if (i.type == INT) {
  495.         arg_error_1d("mismatched arguments", n, i);
  496.     } else {
  497.         for (z = 0; z < n.len; ++z) s1d(n.v.vec[z], i.v.vec[z]);
  498.     }
  499.     }
  500. }
  501.  
  502. /* This is identical to set_1d_n, but puts stuff into ttypes array instead. */
  503.  
  504. #define s1dt(n,i) (*((short *)((char *)((char *)&(ttypes[i])) + offset)) = n)
  505.  
  506. set_1d_t(n, i, offset)
  507. Obj n, i;
  508. int offset;
  509. {
  510.     int z;
  511.  
  512.     if (n.type == INT) {
  513.     if (i.type == INT) {
  514.         s1dt(n.v.num, i.v.num);
  515.     } else {
  516.         for (z = 0; z < i.len; ++z)    s1dt(n.v.num, i.v.vec[z]);
  517.     }
  518.     } else {
  519.     if (i.type == INT) {
  520.         arg_error_1d("mismatched arguments", n, i);
  521.     } else {
  522.         for (z = 0; z < n.len; ++z) s1dt(n.v.vec[z], i.v.vec[z]);
  523.     }
  524.     }
  525. }
  526.  
  527. /* Dump the offending values and leave - any attempt to keep going will */
  528. /* probably screw things up and maybe cause a core dump. */
  529.  
  530. arg_error_1d(msg, a, b)
  531. char *msg;
  532. Obj a, b;
  533. {
  534.     fprintf(stderr, "Error in array setting: %s\n", msg);
  535.     print_object(a, TRUE);
  536.     print_object(b, TRUE);
  537.     exit(1);
  538. }
  539.  
  540. /* Two-dimensional setting is more complicated.  VSS is not useful, and VVV */
  541. /* can only set diagonals.  All others are useful. */
  542.  
  543. #define s2d(n,i,j) (*((short *)((char *)((char *)&(utypes[j]))+offset+2*i))=n)
  544.  
  545. set_2d(n, i1, i2, offset)
  546. Obj n, i1, i2;
  547. int offset;
  548. {
  549.     int z, zz;
  550.  
  551.     if (n.type == INT) {
  552.     if (i1.type == INT) {
  553.         if (i2.type == INT) {
  554.         s2d(n.v.num, i1.v.num, i2.v.num);
  555.         } else {
  556.         for (z = 0; z < i2.len; ++z) {
  557.             s2d(n.v.num, i1.v.num, i2.v.vec[z]);
  558.         }
  559.         }
  560.     } else {
  561.         if (i2.type == INT) {
  562.         for (z = 0; z < i1.len; ++z) {
  563.             s2d(n.v.num, i1.v.vec[z], i2.v.num);
  564.         }
  565.         } else {
  566.         for (z = 0; z < i1.len; ++z) {
  567.             for (zz = 0; zz < i2.len; ++zz) {
  568.             s2d(n.v.num, i1.v.vec[z], i2.v.vec[zz]);
  569.             }
  570.         }
  571.         }
  572.     }
  573.     } else {
  574.     if (i1.type == INT) {
  575.         if (i2.type == INT) {
  576.         arg_error_2d("mismatched argument types", n, i1, i2);
  577.         } else {
  578.         if (n.len != i2.len)
  579.             arg_error_2d("mismatched vectors", n, i1, i2);
  580.         for (z = 0; z < i2.len; ++z) {
  581.             s2d(n.v.vec[z], i1.v.num, i2.v.vec[z]);
  582.         }
  583.         }
  584.     } else {
  585.         if (i2.type == INT) {
  586.         if (n.len != i1.len)
  587.             arg_error_2d("mismatched vectors", n, i1, i2);
  588.         for (z = 0; z < i1.len; ++z) {
  589.             s2d(n.v.vec[z], i1.v.vec[z], i2.v.num);
  590.         }
  591.         } else {
  592.         arg_error_2d("mismatched argument types", n, i1, i2);
  593.         }
  594.     }
  595.     }
  596. }
  597.  
  598. /* Dump the offending values and die. */
  599.  
  600. arg_error_2d(msg, a, b, c)
  601. char *msg;
  602. Obj a, b, c;
  603. {
  604.     fprintf(stderr, "Error in array setting: %s\n", msg);
  605.     print_object(a, TRUE);
  606.     print_object(b, TRUE);
  607.     print_object(c, TRUE);
  608.     exit(1);
  609. }
  610.  
  611. /* Fortunately there are no 3D arrays!! */
  612.  
  613. /* The dreary wasteland of implementations of words. */
  614. /* Fortunately, most of them can use the multi-dimensional array hacking as */
  615. /* defined already, but we have to have both a "setter" function and a */
  616. /* "dictionary" function, so lots of short functions :-(. */
  617.  
  618. /* Build an arbitrary integer vector using [ ] words. */
  619.  
  620. start_vector()
  621. {
  622.     stack[top].type = STR;
  623.     stack[top++].v.str = "start-vector";  /* check overflow? */
  624. }
  625.  
  626. /* Vector creation is a little tricky, since we usually take a variable */
  627. /* number of words from the stack.  The basic scheme scans the stack for */
  628. /* for a non-integer or stack bottom, then builds a vector at that point. */
  629.  
  630. finish_vector()
  631. {
  632.     int i, bot, size;
  633.  
  634.     for (i = top-1; i >= 0; --i) {
  635.     if (stack[i].type != INT) {
  636.         bot = i;
  637.         break;
  638.     }
  639.     }
  640.     size = top - bot - 1;
  641.     if (size <= MAXUTYPES) {
  642.     stack[bot].type = VEC;
  643.     stack[bot].len = size;
  644.     for (i = 0; i < size; ++i) {
  645.         stack[bot].v.vec[i] = stack[bot+i+1].v.num;
  646.     }
  647.     top = bot + 1;
  648.     } else {
  649.     fprintf(stderr, "Vector too big!\n");
  650.     exit(1);
  651.     }
  652. }
  653.  
  654. /* Assign a value to a variable, basically.  Actually, we're defining a new */
  655. /* word and filling its value slot. */
  656.  
  657. set_define(a1, a2)
  658. Obj a1, a2;
  659. {
  660.     words[add_word(a2.v.str, VV, 0, NULL)].f.value = a1;
  661. }
  662.  
  663. push_true() { push_number(TRUE); }
  664.  
  665. push_false() { push_number(FALSE); }
  666.  
  667. push_nothing() { push_number(NOTHING); }
  668.  
  669. /* Things that don't fit in with all the "short" slots. */
  670.  
  671. set_periodname(a1) Obj a1; { period.name = a1.v.str; }
  672.  
  673. set_fontname(a1) Obj a1; { period.fontname = a1.v.str; }
  674.  
  675. /* Add a new type of unit.  All args must be strings, but after the defn, */
  676. /* both char and full name may be used as normal words. */
  677.  
  678. define_utype(a1, a2, a3)
  679. Obj a1, a2, a3;
  680. {
  681.     Obj obj;
  682.  
  683.     if (a1.v.str[0] == '.' || a1.v.str[0] == '?') {
  684.       fprintf(stderr, "'.' and '?' can not be unit characters.\n");
  685.       exit(1);
  686.     }
  687.     if (period.numutypes < MAXUTYPES) {
  688.     obj.type = INT;
  689.     obj.v.num = period.numutypes;
  690.     utypes[period.numutypes].uchar = a1.v.str[0];
  691.     utypes[period.numutypes].name  = a2.v.str;
  692.     utypes[period.numutypes].help  = a3.v.str;
  693.     words[add_word(a1.v.str, VV, 0, NULL)].f.value = obj;
  694.     words[add_word(a2.v.str, VV, 0, NULL)].f.value = obj;
  695.     period.numutypes++;
  696.     } else {
  697.     fprintf(stderr, "Limited to %d types of units! (failed on %s)\n",
  698.         MAXUTYPES, a2.v.str);
  699.     exit(1);
  700.     }
  701. }
  702.  
  703. /* Add a new type of resource (similar to adding unit type). */
  704.  
  705. define_rtype(a1, a2, a3)
  706. Obj a1, a2, a3;
  707. {
  708.     Obj obj;
  709.  
  710.     if (period.numrtypes < MAXRTYPES) {
  711.     obj.type = INT;
  712.     obj.v.num = period.numrtypes;
  713.     rtypes[period.numrtypes].rchar = a1.v.str[0];
  714.     rtypes[period.numrtypes].name  = a2.v.str;
  715.     rtypes[period.numrtypes].help  = a3.v.str;
  716.     words[add_word(a1.v.str, VV, 0, NULL)].f.value = obj;
  717.     words[add_word(a2.v.str, VV, 0, NULL)].f.value = obj;
  718.     period.numrtypes++;
  719.     } else {
  720.     fprintf(stderr, "Limited to %d types of resources! (failed on %s)\n",
  721.         MAXRTYPES, a2.v.str);
  722.     exit(1);
  723.     }
  724. }
  725.  
  726. /* Add a new type of terrain.  All args must be strings, but after the defn, */
  727. /* only full name may be used as a normal word. (Since most terrain chars */
  728. /* seem to conflict with various important chars in this language.) */
  729.  
  730. define_ttype(a1, a2, a3)
  731. Obj a1, a2, a3;
  732. {
  733.     Obj obj;
  734.  
  735.     if (period.numttypes < MAXTTYPES) {
  736.     obj.type = INT;
  737.     obj.v.num = period.numttypes;
  738.     ttypes[period.numttypes].tchar = a1.v.str[0];
  739.     ttypes[period.numttypes].name  = a2.v.str;
  740.     ttypes[period.numttypes].color = a3.v.str;
  741.     /* Terrain chars are weird, so don't add as new words */
  742.     words[add_word(a2.v.str, VV, 0, NULL)].f.value = obj;
  743.     period.numttypes++;
  744.     } else {
  745.     fprintf(stderr, "Limited to %d types of terrain! (failed on %s)\n",
  746.         MAXTTYPES, a2.v.str);
  747.     exit(1);
  748.     }
  749. }
  750.  
  751. /* Push vector of all types of objects for various classes. */
  752.  
  753. push_u_vec() { push_iota_vec(period.numutypes); }
  754.  
  755. push_r_vec() { push_iota_vec(period.numrtypes); }
  756.  
  757. push_t_vec() { push_iota_vec(period.numttypes); }
  758.  
  759. /* Simulate the "iota" operator of APL; push a vector of consecutive ints. */
  760.  
  761. push_iota_vec(n)
  762. int n;
  763. {
  764.     int i;
  765.  
  766.     stack[top].type = VEC;
  767.     stack[top].len = n;
  768.     for (i = 0; i < n; ++i) stack[top].v.vec[i] = i;
  769.     ++top;
  770. }
  771.  
  772. /* Add the string into the array of random side names. */
  773.  
  774. add_side_name(a1)
  775. Obj a1;
  776. {
  777.     snames[period.numsnames++] = a1.v.str;
  778. }
  779.  
  780. /* Add the string into the array of random unit names. */
  781.  
  782. add_unit_name(a1)
  783. Obj a1;
  784. {
  785.     unames[period.numunames++] = a1.v.str;
  786. }
  787.  
  788. /* Absorb designer notes directly instead of trying to interpret words or */
  789. /* strings or whatever.  This version limits commentary to about 8 pages, */
  790. /* which should be more than enough. */
  791.  
  792. begin_notes()
  793. {
  794.     char *line;
  795.     int i = 0;
  796.  
  797.     period.notes = (char **) malloc(400 * sizeof(char *));
  798.     while ((line = read_line(pfp)) != NULL) {
  799.     if (strcmp(line, "end{notes}") == 0) break;
  800.     period.notes[i++] = line;
  801.     if (i >= 399) break;
  802.     }
  803.     period.notes[i] = NULL;
  804. }
  805.  
  806. /* Print the whole stack, attempting to fit it all on one line (hah!). */
  807.  
  808. print_stack()
  809. {
  810.     int i;
  811.  
  812.     if (top == 0) {
  813.     printf("/* Stack is empty. */\n");
  814.     } else {
  815.     printf("/* Stack ");
  816.     for (i = 0; i < top; ++i) {
  817.         print_object(stack[i], FALSE);
  818.     }
  819.     printf("*/\n");
  820.     }
  821. }
  822.  
  823. /* Ah, the joys of polymorphism.  Gotta print each type out differently. */
  824. /* I suppose it's better than not knowing what the types of things are... */
  825.  
  826. print_object(obj, newline)
  827. Obj obj;
  828. bool newline;
  829. {
  830.     int i;
  831.  
  832.     switch (obj.type) {
  833.     case INT:
  834.     printf("%d ", obj.v.num);
  835.     break;
  836.     case STR:
  837.     printf("\"%s\" ", obj.v.str);
  838.     break;
  839.     case SYM:
  840.     printf("%s ", words[obj.v.sym].name);
  841.     break;
  842.     case VEC:
  843.     printf("[");
  844.     for (i = 0; i < obj.len; ++i) printf("%d,", obj.v.vec[i]);
  845.     printf("] ");
  846.     break;
  847.     }
  848.     if (newline) printf("\n");
  849. }
  850.  
  851. /* Build the initial dictionary.  At the end of this file, to eliminate need */
  852. /* for a zillion function declarations! */
  853.  
  854. init_words()
  855. {
  856.     numwords = 0;
  857.     mustbenew = FALSE;   /* efficiency hack */
  858.     add_word("[", FN, 0, start_vector);
  859.     add_word("]", FN, 0, finish_vector);
  860.     add_word("true", FN, 0, push_true);
  861.     add_word("false", FN, 0, push_false);
  862.     add_word("define", FN, 2, set_define);
  863.     add_word("period-name", FN, 1, set_periodname);
  864.     add_word("font-name", FN, 1, set_fontname);
  865.     add_word("utype", FN, 3, define_utype);
  866.     add_word("rtype", FN, 3, define_rtype);
  867.     add_word("ttype", FN, 3, define_ttype);
  868.     add_word("u*", FN, 0, push_u_vec);
  869.     add_word("r*", FN, 0, push_r_vec);
  870.     add_word("t*", FN, 0, push_t_vec);
  871.     add_word("nothing", FN, 0, push_nothing);
  872.     add_word("dark", T1, 2, OFFSET(Ttype, dark));
  873.     add_word("nuked", T1, 2, OFFSET(Ttype, nuked));
  874.     add_word("max-alt", T1, 2, OFFSET(Ttype, maxalt));
  875.     add_word("min-alt", T1, 2, OFFSET(Ttype, minalt));
  876.     add_word("max-wet", T1, 2, OFFSET(Ttype, maxwet));
  877.     add_word("min-wet", T1, 2, OFFSET(Ttype, minwet));
  878.     add_word("default-terrain", P0, 1, OFFSET(Period, defaultterrain));
  879.     add_word("edge-terrain", P0, 1, OFFSET(Period, edgeterrain));
  880.     add_word("alt-roughness", P0, 1, OFFSET(Period, altroughness));
  881.     add_word("wet-roughness", P0, 1, OFFSET(Period, wetroughness));
  882.     add_word("machine-advantage", P0, 1, OFFSET(Period, machineadvantage));
  883.     add_word("icon-name", S1, 2, OFFSET(Utype, bitmapname));
  884.     add_word("neutral", U1, 2, OFFSET(Utype, isneutral));
  885.     add_word("territory", U1, 2, OFFSET(Utype, territory));
  886.     add_word("can-disband", U1, 2, OFFSET(Utype, disband));
  887.     add_word("efficiency", P0, 1, OFFSET(Period, efficiency));
  888.     add_word("country-size", P0, 1, OFFSET(Period, countrysize));
  889.     add_word("country-min-distance", P0, 1, OFFSET(Period, mindistance));
  890.     add_word("country-max-distance", P0, 1, OFFSET(Period, maxdistance));
  891.     add_word("first-unit", P0, 1, OFFSET(Period, firstutype));
  892.     add_word("first-product", P0, 1, OFFSET(Period, firstptype));
  893.     add_word("in-country", U1, 2, OFFSET(Utype, incountry));
  894.     add_word("density", U1, 2, OFFSET(Utype, density));
  895.     add_word("named", U1, 2, OFFSET(Utype, named));
  896.     add_word("already-seen", U1, 2, OFFSET(Utype, alreadyseen));
  897.     add_word("favored", U2, 3, VOFFSET(Utype, favored));
  898.     add_word("stockpile", U2, 3, VOFFSET(Utype, stockpile));
  899.     add_word("known-radius", P0, 1, OFFSET(Period, knownradius));
  900.     add_word("spy-chance", P0, 1, OFFSET(Period, spychance));
  901.     add_word("spy-quality", P0, 1, OFFSET(Period, spyquality));
  902.     add_word("leave-map", P0, 1, OFFSET(Period, leavemap));
  903.     add_word("repair-scale", P0, 1, OFFSET(Period, repairscale));
  904.     add_word("revolt", U1, 2, OFFSET(Utype, revolt));
  905.     add_word("surrender", U1, 2, OFFSET(Utype, surrender));
  906.     add_word("siege", U1, 2, OFFSET(Utype, siege));
  907.     add_word("attrition", U2, 3, VOFFSET(Utype, attrition));
  908.     add_word("attrition-damage", U1, 2, OFFSET(Utype, attdamage));
  909.     add_word("attrition-message", S1, 2, OFFSET(Utype, attritionmsg));
  910.     add_word("accident", U2, 3, VOFFSET(Utype, accident));
  911.     add_word("accident-message", S1, 2, OFFSET(Utype, accidentmsg));
  912.     add_word("make", U2, 3, VOFFSET(Utype, make));
  913.     add_word("maker", U1, 2, OFFSET(Utype, maker));
  914.     add_word("occupant-produce", U1, 2, OFFSET(Utype, occproduce));
  915.     add_word("startup", U1, 2, OFFSET(Utype, startup));
  916.     add_word("research", U1, 2, OFFSET(Utype, research));
  917.     add_word("research-contrib", U2, 3, VOFFSET(Utype, research_contrib));
  918.     add_word("to-make", U2, 3, VOFFSET(Utype, tomake));
  919.     add_word("repair", U2, 3, VOFFSET(Utype, repair));
  920.     add_word("produce", U2, 3, VOFFSET(Utype, produce));
  921.     add_word("productivity", U2, 3, VOFFSET(Utype, productivity));
  922.     add_word("storage", U2, 3, VOFFSET(Utype, storage));
  923.     add_word("consume", U2, 3, VOFFSET(Utype, consume));
  924.     add_word("in-length", U2, 3, VOFFSET(Utype, inlength));
  925.     add_word("out-length", U2, 3, VOFFSET(Utype, outlength));
  926.     add_word("consume-as-occupant", U1, 2, OFFSET(Utype, consume_as_occupant));
  927.     add_word("survival", U1, 2, OFFSET(Utype, survival));
  928.     add_word("starve-message", S1, 2, OFFSET(Utype, starvemsg));
  929.     add_word("speed", U1, 2, OFFSET(Utype, speed));
  930.     add_word("moves", U2, 3, VOFFSET(Utype, moves));
  931.     add_word("random-move", U2, 3, VOFFSET(Utype, randommove));
  932.     add_word("to-move", U2, 3, VOFFSET(Utype, tomove));
  933.     add_word("capacity", U2, 3, VOFFSET(Utype, capacity));
  934.     add_word("hold-volume", U1, 2, OFFSET(Utype, holdvolume));
  935.     add_word("volume", U1, 2, OFFSET(Utype, volume));
  936.     add_word("enter-time", U2, 3, VOFFSET(Utype, entertime));
  937.     add_word("leave-time", U2, 3, VOFFSET(Utype, leavetime));
  938.     add_word("bridge", U2, 3, VOFFSET(Utype, bridge));
  939.     add_word("alter-mobility", U2, 3, VOFFSET(Utype, mobility));
  940.     add_word("see-best", U1, 2, OFFSET(Utype, seebest));
  941.     add_word("see-worst", U1, 2, OFFSET(Utype, seeworst));
  942.     add_word("see-range", U1, 2, OFFSET(Utype, seerange));
  943.     add_word("visibility", U1, 2, OFFSET(Utype, visibility));
  944.     add_word("conceal", U2, 3, VOFFSET(Utype, conceal));
  945.     add_word("always-seen", U1, 2, OFFSET(Utype, seealways));
  946.     add_word("all-seen", P0, 1, OFFSET(Period, allseen));
  947.     add_word("hp", U1, 2, OFFSET(Utype, hp));
  948.     add_word("crippled", U1, 2, OFFSET(Utype, crippled));
  949.     add_word("hit", U2, 3, VOFFSET(Utype, hit));
  950.     add_word("neutrality", P0, 1, OFFSET(Period, neutrality));
  951.     add_word("defense", U2, 3, VOFFSET(Utype, defense));
  952.     add_word("damage", U2, 3, VOFFSET(Utype, damage));
  953.     add_word("can-counter", U1, 2, OFFSET(Utype, counterable));
  954.     add_word("area-radius", U1, 2, OFFSET(Utype, arearadius));
  955.     add_word("nuke-hit", P0, 1, OFFSET(Period, nukehit));
  956.     add_word("self-destruct", U1, 2, OFFSET(Utype, selfdestruct));
  957.     add_word("combat-time", U1, 2, OFFSET(Utype, hittime));
  958.     add_word("counterattack", P0, 1, OFFSET(Period, counterattack));
  959.     add_word("capturemoves", P0, 1, OFFSET(Period, capturemoves));
  960.     add_word("capture", U2, 3, VOFFSET(Utype, capture));
  961.     add_word("guard", U2, 3, VOFFSET(Utype, guard));
  962.     add_word("protect", U2, 3, VOFFSET(Utype, protect));
  963.     add_word("changes-side", U1, 2, OFFSET(Utype, changeside));
  964.     add_word("retreat", U1, 2, OFFSET(Utype, retreat));
  965.     add_word("hits-with", U2, 3, VOFFSET(Utype, hitswith));
  966.     add_word("hit-by", U2, 3, VOFFSET(Utype, hitby));
  967.     add_word("destroy-message", S1, 2, OFFSET(Utype, destroymsg));
  968.     add_word("clear-side-names", FN, 0, clear_side_names);
  969.     add_word("clear-unit-names", FN, 0, clear_unit_names);
  970.     add_word("sname", FN, 1, add_side_name);
  971.     add_word("uname", FN, 1, add_unit_name);
  972.     add_word("begin{notes}", FN, 0, begin_notes);
  973.     add_word("print", FN, 0, print_stack);
  974.     add_word("attack-worth", U1, 2, OFFSET(Utype, attack_worth));
  975.     add_word("defense-worth", U1, 2, OFFSET(Utype, defense_worth));
  976.     add_word("explore-worth", U1, 2, OFFSET(Utype, explore_worth));
  977.     add_word("min-region-size", U1, 2, OFFSET(Utype, min_region_size));
  978.     add_word("end", FN, 0, NULL);
  979.     mustbenew = TRUE;
  980. }
  981.  
  982. /* Run a doublecheck on plausibility of period parameters.  Additional */
  983. /* checks are performed elsewhere as needed, for instance during random */
  984. /* generation.  Serious mistakes exit now, since they can cause all sorts */
  985. /* of strange behavior and core dumps.  It's a little more friendly to only */
  986. /* exit at the end of the tests, so all the mistakes can be found at once. */
  987.  
  988. check_period_numbers()
  989. {
  990.     bool failed = FALSE, movers = FALSE, makers = FALSE;
  991.     int u1, u2, t1, t2;
  992.  
  993.     if (period.numutypes < 1) {
  994.     fprintf(stderr, "No units have been defined at all!\n");
  995.     exit(1);
  996.     }
  997.     if (period.numttypes < 1) {
  998.     fprintf(stderr, "No terrain types have been defined at all!\n");
  999.     exit(1);
  1000.     }
  1001.     if (!between(0, period.mindistance, period.maxdistance)) {
  1002.     fprintf(stderr, "Warning: Country distances %d to %d screwed up\n",
  1003.         period.mindistance, period.maxdistance);
  1004.     }
  1005.     for_all_unit_types(u1) {
  1006.     if (utypes[u1].hp <= 0) {
  1007.         fprintf(stderr, "Utype %d has nonpositive hp!\n", u1);
  1008.         failed = TRUE;
  1009.     }
  1010.     if (utypes[u1].speed > 0) {
  1011.         movers = TRUE;
  1012.     }
  1013.     for_all_unit_types(u2) {
  1014.         if (utypes[u1].make[u2] > 0) {
  1015.         if (utypes[u1].maker) makers = TRUE;
  1016.         if (!could_carry(u1, u2) && !could_carry(u2, u1)) {
  1017.             fprintf(stderr,
  1018.                 "Utype %d cannot hold product %d or vice versa!\n",
  1019.                 u1, u2);
  1020.             failed = TRUE;
  1021.         }
  1022.         }
  1023.     }
  1024.       }
  1025.     for_all_terrain_types(t1) {
  1026.     for_all_terrain_types(t2) {
  1027.         if (t1 != t2 && ttypes[t1].tchar == ttypes[t2].tchar) {
  1028.         fprintf(stderr, "Terrain types %d and %d both use '%c'!\n",
  1029.             t1, t2, ttypes[t1].tchar);
  1030.         failed = TRUE;
  1031.         }
  1032.     }
  1033.     }
  1034.     if (!movers && !makers) {
  1035.     fprintf(stderr, "No movers or builders have been defined!\n");
  1036.     failed = TRUE;
  1037.     }
  1038.     if (failed) exit(1);
  1039.     if (Debug) {
  1040.     printf("\n    %d unit types, %d resource types, %d terrain types\n",
  1041.            period.numutypes, period.numrtypes, period.numttypes);
  1042.     }
  1043. }
  1044.