Galactic Bloodshed
racegen.cc
Go to the documentation of this file.
1 // Copyright 2014 The Galactic Bloodshed Authors. All rights reserved.
2 // Use of this source code is governed by a license that can be
3 // found in the COPYING file.
4 
5 #include "gb/racegen.h"
6 
7 #include <unistd.h>
8 
9 #include <cmath>
10 #include <cstdarg>
11 #include <cstdio>
12 #include <cstdlib>
13 #include <cstring>
14 
15 #include "gb/enroll.h"
16 #include "gb/game_info.h"
17 
18 static int do_racegen();
19 
20 #ifdef PRIV /* Extra stuff for privileged racegen */
21 
22 #include <arpa/inet.h>
23 #include <netinet/in.h>
24 #include <sys/socket.h>
25 
26 #endif
27 
28 static int fd;
29 static int isserver = 0;
30 
31 static int critique_modification();
32 static void execute(int argc, char **argv);
33 static void fix_up_iq();
34 static void initialize();
35 static void help(int, char *[]);
36 static void load(int, char *[]);
37 static void metamorph();
38 static int modify(int argc, char *argv[]);
39 static void normal();
40 static void print(int argc, char *argv[]);
41 static void save(int argc, char *argv[]);
42 static void send2(int argc, char *argv[]);
43 static void quit(int argc, char **argv);
44 
45 int main() {
46 #ifdef PRIV
47  int port;
48 
49  /* Check command line syntax */
50 
51  if (argc > 1) {
52  if ((argv[1][0] == '-') && (isserver = (argv[1][1] == 's'))) {
53  if (argc > 2)
54  port = atoi(argv[2]);
55  else
56  port = 2020;
57  if (port == 0) {
58  printf("Syntax: racegen [-s [port]]\n");
59  exit(0);
60  }
61  } else {
62  printf("Syntax: racegen [-s [port]]\n");
63  return (0);
64  }
65  }
66 
67  if (isserver) { /* Server version of racegen */
68  int sockfd;
69  socklen_t clilen;
70  struct sockaddr_in cli_addr, serv_addr;
71 
72  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
73  fprintf(stderr, "server: can't open stream socket");
74  exit(0);
75  }
76  bzero((char *)&serv_addr, sizeof(serv_addr));
77  serv_addr.sin_family = AF_INET;
78  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
79  serv_addr.sin_port = htons(port);
80 
81  if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
82  fprintf(stderr, "server: can't bind local address");
83  exit(0);
84  }
85 
86  listen(sockfd, 5);
87 
88  if (fork()) {
89  printf("Racegen set up on port %d\n", port);
90  printf("Now accepting connections.\n\n");
91  exit(0);
92  }
93 
94  for (;;) {
95  clilen = sizeof(cli_addr);
96  fd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
97  if (fd < 0) fprintf(stderr, "server: accept error");
98  if (fork()) {
99  dup2(fd, 1);
100  dup2(fd, 0);
101  do_racegen();
102  close(fd);
103  exit(0);
104  }
105  close(fd);
106  }
107  } else
108  do_racegen(); /* Non-server enroll version of racegen */
109 
110 #else /* Non-PRIV version */
111  do_racegen();
112 #endif
113  return 0;
114 }
115 
116 static attribute attr[N_ATTRIBUTES] = {
117  {ADVENT,
118  "Adventurism",
119  0.0,
120  0.0,
121  0.0,
122  300.0,
123  0.0,
124  0.05,
125  0.4,
126  0.99,
127  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
128  0},
129  {ABSORB,
130  "Absorb",
131  0.0,
132  0.0,
133  0.0,
134  200.0,
135  0.0,
136  0.00,
137  0.00,
138  1.00,
139  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
140  2},
141  {BIRTH,
142  "Birthrate",
143  0.0,
144  0.0,
145  0.0,
146  500.0,
147  0.0,
148  0.2,
149  0.6,
150  1.0,
151  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
152  0},
153  {COL_IQ,
154  "Collective IQ",
155  0.0,
156  0.0,
157  0.0,
158  -350.5,
159  0.0,
160  0.00,
161  0.00,
162  1.00,
163  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
164  2},
165  {FERT,
166  "Fertilize",
167  200.0,
168  1.0,
169  1.0,
170  300.0,
171  0.0,
172  0.0,
173  0.0,
174  1.0,
175  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
176  0},
177  {A_IQ,
178  "IQ",
179  100.0,
180  0.03,
181  140,
182  6,
183  0.0,
184  50,
185  150,
186  220,
187  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
188  1},
189  {FIGHT,
190  "Fight",
191  10.0,
192  0.4,
193  6.0,
194  65.0,
195  0.0,
196  1,
197  4,
198  20,
199  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
200  1},
201  {PODS,
202  "Pods",
203  0.0,
204  0.0,
205  0.0,
206  200.0,
207  0.0,
208  0.00,
209  0.00,
210  1.00,
211  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
212  2},
213  {MASS,
214  "Mass",
215  100.0,
216  1.0,
217  3.1,
218  -100.0,
219  0.0,
220  0.1,
221  1.0,
222  3.0,
223  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
224  0},
225  {SEXES,
226  "Sexes",
227  2.2,
228  -0.5,
229  9.0,
230  -3.0,
231  0.0,
232  1,
233  2,
234  53,
235  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
236  1},
237  {METAB,
238  "Metabolism",
239  300.0,
240  1,
241  1.3,
242  700.0,
243  0.0,
244  0.1,
245  1.0,
246  4.0,
247  {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
248  0}};
249 
251  "Earth", "Forest", "Desert", "Water", "Airless", "Iceball", "Jovian"};
252 const int planet_cost[N_HOME_PLANET_TYPES] = {75, 50, 50, 50, -25, -25, 600};
253 
254 const char *race_print_name[N_RACE_TYPES] = {"Normal", "Metamorph"};
255 const int race_cost[N_RACE_TYPES] = {0, 0};
256 
257 const char *priv_print_name[N_PRIV_TYPES] = {"God", "Guest", "Normal"};
258 
260  "Water", "Land", "Mountain", "Gas", "Ice", "Forest", "Desert", "Plated"};
261 
262 const int blah[N_SECTOR_TYPES] = {-1, 0, 50, 100, 200, 300, 400, 500};
263 
265  /* . * ^ ~ # ) - o */
266  {
267  0.0,
268  },
269  {.001, 0.0},
270  {
271  .002,
272  -.0005,
273  0.0,
274  },
275  {
276  999,
277  999,
278  999,
279  0.0,
280  },
281  {
282  .001,
283  0.0,
284  -.002,
285  999,
286  0.0,
287  },
288  {
289  0.0,
290  -.001,
291  0.0,
292  999,
293  .001,
294  0.0,
295  },
296  {.003, -.0005, 0.0, 999, 0.0, .001, 0.0},
297  {0.0, 0.0, 0.0, 999, 0.0, 0.0, 0.0, 0.0}};
298 
300  /* . * ^ ~ # ) - o */
301  /* @ */
302  {1.00, 1.00, 2.00, 99.00, 1.01, 1.50, 3.00, 1.01},
303  /* ) */
304  {1.01, 1.50, 2.00, 99.00, 1.01, 1.00, 3.00, 1.01},
305  /* - */
306  {3.00, 1.01, 1.01, 99.00, 1.50, 3.00, 1.00, 1.01},
307  /* . */
308  {1.00, 1.50, 3.00, 99.00, 1.01, 1.01, 3.00, 1.01},
309  /* O */
310  {1.01, 1.00, 1.00, 99.00, 1.01, 1.01, 1.00, 1.01},
311  /* # */
312  {3.00, 1.01, 1.00, 99.00, 1.00, 1.50, 2.00, 1.01},
313  /* ~ */
314  {99.00, 99.00, 99.00, 1.00, 99.00, 99.00, 99.00, 99.00}};
315 
316 /**************
317  * Global variables for this program.
318  */
319 struct x race_info, cost_info, last;
320 
323 int altered = 0; /* 1 iff race has been altered since last saved */
324 int changed = 1; /* 1 iff race has been changed since last printed */
325 int please_quit = 0; /* 1 iff you want to exit ASAP. */
326 
327 /**************
328  * Price function for racegen. Finds the cost of `race', and returns it.
329  * As a side effect the costs for all the individual fields of `race' get
330  * stuffed into `cost' for later printing.
331  */
333  int i;
334  int j;
335  int sum = 0;
336 
337 #define ROUND(f) ((int)(0.5 + (f)))
338  for (i = FIRST_ATTRIBUTE; i <= LAST_ATTRIBUTE; i++)
339  cost_info.attr[i] =
340  (exp(attr[i].e_fudge * (race_info.attr[i] - attr[i].e_hinge)) *
341  attr[i].e_factor +
342  race_info.attr[i] * attr[i].l_factor);
343  for (i = FIRST_ATTRIBUTE; i <= LAST_ATTRIBUTE; i++)
344  for (j = FIRST_ATTRIBUTE; j <= LAST_ATTRIBUTE; j++)
345  if (attr[i].cov[j] != 0.0)
346  cost_info.attr[i] *= (1.0 + attr[i].cov[j] * race_info.attr[j]);
347  for (i = FIRST_ATTRIBUTE; i <= LAST_ATTRIBUTE; i++)
348  sum += (cost_info.attr[i] = ROUND(cost_info.attr[i] + attr[i].l_fudge));
349 
350  sum += (cost_info.home_planet_type = planet_cost[race_info.home_planet_type]);
351  sum += (cost_info.race_type = race_cost[race_info.race_type]);
352  race_info.n_sector_types = 0;
353  for (i = FIRST_SECTOR_TYPE; i <= LAST_SECTOR_TYPE; i++) {
354  if (race_info.compat[i] != 0.0) race_info.n_sector_types += 1;
355  /* Get the base costs: */
356  cost_info.compat[i] =
357  race_info.compat[i] * 0.5 + 10.8 * log(1.0 + race_info.compat[i]);
358  }
359  for (i = FIRST_SECTOR_TYPE; i <= LAST_SECTOR_TYPE; i++)
360  for (j = i + 1; j <= LAST_SECTOR_TYPE; j++)
361  if (compat_cov[j][i] != 0.0) {
362  cost_info.compat[i] *= (1.0 + compat_cov[j][i] * race_info.compat[j]);
363  cost_info.compat[j] *= (1.0 + compat_cov[j][i] * race_info.compat[i]);
364  }
365  for (i = FIRST_SECTOR_TYPE; i <= LAST_SECTOR_TYPE; i++)
366  if (planet_compat_cov[race_info.home_planet_type][i] > 1.01)
367  cost_info.compat[i] *= planet_compat_cov[race_info.home_planet_type][i];
368  for (i = FIRST_SECTOR_TYPE; i <= LAST_SECTOR_TYPE; i++)
369  sum += (cost_info.compat[i] = ROUND(cost_info.compat[i]));
370  sum += (cost_info.n_sector_types = blah[race_info.n_sector_types]);
371  return sum;
372 }
373 
374 static void metamorph() {
375  /* Adventurousness is not correlated with Max IQ. */
376  attr[ADVENT].cov[A_IQ] = 0.00;
377  /* A high birthrate is easier if few sexes, high metab, and low mass.*/
378  /** Morphs are not born, they hatch. THus the mass limitation is smaller:**/
379  attr[BIRTH].cov[MASS] = 0.10 / ATTR_RANGE(MASS);
380  attr[BIRTH].cov[SEXES] = 0.50 / ATTR_RANGE(SEXES);
381  attr[BIRTH].cov[METAB] = -0.10 / ATTR_RANGE(METAB);
382  /* Natural fertilization is ultimately associated with metabolic activity */
383  /** Morphs are naturally adept at fertilization. **/
384  attr[FERT].cov[METAB] = -0.30 / ATTR_RANGE(METAB);
385  /* Fighting is easier for independent, high-mass, high-metab races: */
386  attr[FIGHT].cov[ADVENT] = -0.10 / ATTR_RANGE(ADVENT);
387  attr[FIGHT].cov[MASS] = -0.20 / ATTR_RANGE(MASS);
388  attr[FIGHT].cov[METAB] = -0.15 / ATTR_RANGE(METAB);
389  /* A high metabolism is hard to support if you have high mass: */
390  /** Due to general squishiness, this effect is not as strong for mesos. **/
391  attr[METAB].cov[MASS] = 0.05 / ATTR_RANGE(MASS);
392  /* However, a large IQ is easier with high mass; (lot of brain space): */
393  /** IQ represents max IQ, thus, no go. **/
394  attr[A_IQ].cov[MASS] = 0.00;
395  /* IQ is more expensive due to collective intelligence: */
396  attr[A_IQ].cov[COL_IQ] = 0.00 / ATTR_RANGE(COL_IQ);
397 
398  strcpy(attr[A_IQ].print_name, "IQ Limit");
399 }
400 
401 static void normal() {
402  /* Adventurousness is more likely with people smart enough to do it. */
403  attr[ADVENT].cov[A_IQ] = -0.40 / ATTR_RANGE(A_IQ);
404  /* Birthrate is easier if few sexes, high metab, low iq, and low mass.*/
405  attr[BIRTH].cov[A_IQ] = 0.20 / ATTR_RANGE(A_IQ);
406  attr[BIRTH].cov[MASS] = 0.40 / ATTR_RANGE(MASS);
407  attr[BIRTH].cov[SEXES] = 0.90 / ATTR_RANGE(SEXES);
408  attr[BIRTH].cov[METAB] = -0.20 / ATTR_RANGE(METAB);
409  /* Natural fertilization is ultimately associated with metabolic activity */
410  attr[FERT].cov[METAB] = -0.20 / ATTR_RANGE(METAB);
411  /* Fighting is easier for independent, high-mass, high-metab races: */
412  attr[FIGHT].cov[A_IQ] = -0.20 / ATTR_RANGE(A_IQ);
413  attr[FIGHT].cov[ADVENT] = -0.05 / ATTR_RANGE(ADVENT);
414  attr[FIGHT].cov[MASS] = -0.20 / ATTR_RANGE(MASS);
415  attr[FIGHT].cov[METAB] = -0.05 / ATTR_RANGE(METAB);
416  /* A high metabolism is hard to support if you have high mass: */
417  attr[METAB].cov[MASS] = 0.15 / ATTR_RANGE(MASS);
418  attr[METAB].cov[A_IQ] = -0.10 / ATTR_RANGE(A_IQ);
419  /* However, a large IQ is easier with high mass; (lot of brain space): */
420  attr[A_IQ].cov[MASS] = -0.25 / ATTR_RANGE(MASS);
421 
422  strcpy(attr[A_IQ].print_name, "IQ");
423 }
424 
425 static void fix_up_iq() {
426  if (race_info.attr[COL_IQ] == 1.0)
427  strcpy(attr[A_IQ].print_name, "IQ Limit");
428  else
429  strcpy(attr[A_IQ].print_name, "IQ");
430 }
431 
432 /**************
433  * VERY IMPORTANT FUNCTION: this function is a representation function
434  * for the race datatype. That is, it will return a positive integer iff
435  * the race is NOT valid. It is thus useful both when modifying a race and
436  * when loading a race. The file f should be nullptr if you do not want to
437  * print out descriptions of the errors; otherwise, error message(s) will be
438  * printed to that file.
439  */
440 int critique_to_file(FILE *f, int rigorous_checking, int is_player_race) {
441  int i;
442  int nerrors = 0;
443 
444 #define FPRINTF
445  if (f != nullptr) fprintf
446 
447  /*
448  * Check for valid attributes: */
449  for (i = FIRST_ATTRIBUTE; i <= LAST_ATTRIBUTE; i++) {
450  if ((attr[i].is_integral == 2) && (race_info.attr[i] != 0.0) &&
451  (race_info.attr[i] != 1.0)) {
452  FPRINTF(f, "%s is boolean valued. Use \"yes\" or \"no\".\n",
453  attr[i].print_name);
454  nerrors += 1;
455  }
456  if (race_info.attr[i] < attr[i].minimum) {
457  FPRINTF(f, "%s must be at least %0.2f.\n", attr[i].print_name,
458  attr[i].minimum);
459  nerrors += 1;
460  }
461  if (race_info.attr[i] > attr[i].maximum) {
462  FPRINTF(f, "%s may be at most %0.2f.\n", attr[i].print_name,
463  attr[i].maximum);
464  nerrors += 1;
465  }
466  /* Warning, but no error: */
467  if (attr[i].is_integral) {
468  if (race_info.attr[i] != ((double)((int)race_info.attr[i])))
469  FPRINTF(f, "%s is integral; truncated to %1.0f.\n", attr[i].print_name,
470  race_info.attr[i] = ((double)((int)race_info.attr[i])));
471  } else if (race_info.attr[i] !=
472  (((double)((int)(100.0 * race_info.attr[i]))) / 100.0))
473  FPRINTF(f, "%s truncated to next lowest hundredth (%1.2f).\n",
474  attr[i].print_name,
475  race_info.attr[i] =
476  (((double)((int)(100.0 * race_info.attr[i]))) / 100.0));
477  }
478 
479  /*
480  * Check for valid normal race attributes: */
481  if (race_info.race_type == R_NORMAL) {
482  if (race_info.attr[ABSORB] != 0.0) {
483  FPRINTF(f, "Normal races do not absorb their enemies in combat.\n");
484  nerrors += 1;
485  }
486  if (race_info.attr[PODS] != 0.0) {
487  FPRINTF(f, "Normal races do not make pods.\n");
488  nerrors += 1;
489  }
490  }
491 
492  /*
493  * Check for valid name: */
494  if (0 == strlen(race_info.name)) {
495  FPRINTF(f, "Use a non-empty name.\n");
496  nerrors += 1;
497  }
498 
499  /*
500  * Check for valid privileges: */
501  if ((race_info.priv_type < FIRST_PRIV_TYPE) ||
502  (race_info.priv_type > LAST_PRIV_TYPE)) {
503  FPRINTF(f, "Privileges out of valid range.\n");
504  nerrors += 1;
505  }
506  if ((race_info.priv_type != P_NORMAL) && is_player_race) {
507  FPRINTF(f, "Players may not create %s races.\n",
508  priv_print_name[race_info.priv_type]);
509  nerrors += 1;
510  }
511 
512  /*
513  * Check for valid home planet: */
514  if ((race_info.home_planet_type < FIRST_HOME_PLANET_TYPE) ||
516  FPRINTF(f, "Home planet type out of valid range.\n");
517  nerrors += 1;
518  }
519 
520  /*
521  * Check for valid race: */
522  if ((race_info.race_type < FIRST_RACE_TYPE) ||
523  (race_info.race_type > LAST_RACE_TYPE)) {
524  FPRINTF(f, "Home planet type out of valid range.\n");
525  nerrors += 1;
526  }
527 
528  /*
529  * Check for valid sector compats: */
530  if ((race_info.home_planet_type != H_JOVIAN) &&
531  (race_info.compat[S_PLATED] != 100.0)) {
532  FPRINTF(f, "Non-jovian races must have 100%% plated compat.\n");
533  nerrors += 1;
534  }
535  for (i = FIRST_SECTOR_TYPE + 1; i <= LAST_SECTOR_TYPE; i++) {
536  if (race_info.compat[i] < 0.0) {
537  FPRINTF(f, "Sector compatibility is at minimum 0%%.\n");
538  nerrors += 1;
539  }
540  if (race_info.compat[i] > 100.0) {
541  FPRINTF(f, "Sector compatibility may be at most 100%%.\n");
542  nerrors += 1;
543  }
544  if ((i == S_GAS) && (race_info.compat[i] != 0.0) &&
545  (race_info.home_planet_type != H_JOVIAN)) {
546  FPRINTF(f, "Non-jovian races may never have gas compatibility!\n");
547  nerrors += 1;
548  }
549  if ((i != S_GAS) && (race_info.compat[i] != 0.0) &&
550  (race_info.home_planet_type == H_JOVIAN)) {
551  FPRINTF(f, "Jovian races may have no compatibility other than gas!\n");
552  nerrors += 1;
553  }
554  /* A warning, but no error: */
555  if (race_info.compat[i] != ((double)((int)race_info.compat[i])))
556  FPRINTF(f, "Sector compatibilities are integral; truncated to %1.0f.\n",
557  race_info.compat[i] = ((double)((int)race_info.compat[i])));
558  }
559 
560  if (rigorous_checking) {
561  /*
562  * Any rejection notice is an error: */
563  if (strlen(race_info.rejection)) {
564  FPRINTF(f, "%s", race_info.rejection);
565  nerrors += 1;
566  }
567  /*
568  * Check for valid password: */
569  if (MIN_PASSWORD_LENGTH > strlen(race_info.password)) {
570  FPRINTF(f, "Passwords are required to be at least %d characters long.\n",
572  nerrors += 1;
573  } else if (!strcmp(race_info.password, "XXXX")) {
574  FPRINTF(f, "You must change your password from the default.\n");
575  nerrors += 1;
576  }
577  if (!strcmp(race_info.address, "Unknown")) {
578  FPRINTF(f, "You must change your email address.\n");
579  nerrors += 1;
580  }
581  /*
582  * Check that race isn't 'superrace': */
583  if (npoints < 0) {
584  FPRINTF(f, "You can't have negative points left!\n");
585  nerrors += 1;
586  }
587  /*
588  * Check that sector compats are reasonable. */
589  if ((race_info.home_planet_type != H_JOVIAN) &&
590  (race_info.n_sector_types == 1)) {
591  FPRINTF(f,
592  "Non-jovian races must be compat with at least one sector "
593  "type besides plated.\n");
594  nerrors += 1;
595  }
596  for (i = FIRST_SECTOR_TYPE; i <= LAST_SECTOR_TYPE; i++)
597  if ((planet_compat_cov[race_info.home_planet_type][i] == 1.0) &&
598  (race_info.compat[i] == 100.0))
599  break;
600  if (i == N_SECTOR_TYPES) {
601  FPRINTF(f,
602  "You must have 100%% compat with at least one sector type "
603  "that is common on\n your home planet type. (Marked with a "
604  "'*')\n");
605  nerrors += 1;
606  }
607  }
608  if (race_info.status >= 0)
609  race_info.status = (nerrors == 0) ? STATUS_BALANCED : STATUS_UNBALANCED;
610  return nerrors;
611 #undef FPRINTF
612 }
613 
614 static int critique_modification() {
615  int nerrors;
616 
617  race_info.rejection[0] = '\0';
618  nerrors = critique_to_file(stdout, 0, IS_PLAYER);
619  if (nerrors)
620  bcopy(&last, &race_info, sizeof(struct x));
621  else
622  changed = altered = 1;
623  race_info.status = (nerrors == 0) ? STATUS_BALANCED : STATUS_UNBALANCED;
624  return nerrors;
625 }
626 
627 /**************
628  * Initialize the race to the init value, and set the l_fudge values
629  * accordingly so that the cost of this race's attributes is zero.
630  */
631 static void initialize() {
632  int i;
633 
634  bzero(&race_info, sizeof(race_info));
635  for (i = FIRST_ATTRIBUTE; i <= LAST_ATTRIBUTE; i++)
636  race_info.attr[i] = attr[i].init;
637  race_info.race_type = R_NORMAL;
638  race_info.priv_type = P_NORMAL;
639  race_info.home_planet_type = H_EARTH;
640  race_info.n_sector_types = 1;
641  race_info.compat[S_PLATED] = 100;
642  strcpy(race_info.name, "Unknown");
643  strcpy(race_info.address, "Unknown");
644  strcpy(race_info.password, "XXXX");
645  normal();
646  bcopy(&race_info, &last, sizeof(struct x));
648  for (i = FIRST_ATTRIBUTE; i <= LAST_ATTRIBUTE; i++)
649  attr[i].l_fudge += -cost_info.attr[i];
651 #ifdef ENROLL
652  init_enroll();
653 #endif
654 }
655 
656 /**************
657  * Now the functions that implement racegen's commands. Rather than me
658  * trying to tell you about them here, just run the program and diddle
659  * with it to get the idea.
660  */
661 static void help(int argc, char *argv[]) {
662  int enroll;
663  int process;
664  int i;
665  int j;
666  int helpp;
667  int load;
668  int modify;
669  int print;
670  int save;
671  int send2;
672  int quit;
673 
674  if (argc == 1) {
675  enroll = process = helpp = load = modify = print = save = send2 = quit = 1;
676  printf("\n");
677  printf(
678  "To execute a command, type it at the command line. All commands\n");
679  printf("and command arguments maybe either upper or lower case, and/or\n");
680  printf("abbreviated. The available commands are:\n");
681  } else {
682  enroll = process = helpp = load = modify = print = save = send2 = quit = 0;
683  for (i = 1; i < argc; i++) {
684  j = strlen(argv[i]);
685 #ifdef PRIV
686  if (!strncasecmp(argv[i], "enroll", j) && (!isserver))
687  enroll = 1;
688  else
689 #endif
690  if (!strncasecmp(argv[i], "help", j))
691  helpp = 1;
692  else if (!strncasecmp(argv[i], "load", j) && (!isserver))
693  load = 1;
694  else if (!strncasecmp(argv[i], "modify", j))
695  modify = 1;
696  else if (!strncasecmp(argv[i], "print", j))
697  print = 1;
698 #ifdef PRIV
699  else if (!strncasecmp(argv[i], "process", j) && (!isserver))
700  process = 1;
701 #endif
702  else if (!strncasecmp(argv[i], "save", j) && (!isserver))
703  save = 1;
704  else if (!strncasecmp(argv[i], "send", j))
705  send2 = 1;
706  else if (!strncasecmp(argv[i], "quit", j))
707  quit = 1;
708  else {
709  printf("\n");
710  printf("\"%s\" is not a command.\n", argv[i]);
711  }
712  }
713  }
714 
715 #ifdef PRIV
716  if (enroll) {
717  printf("\n");
718  printf("Enroll\n");
719  printf("\t\t This command will add the current race to the game,\n");
720  printf("\t\t after checking to make sure it has all points spent, and\n");
721  printf("\t\t other such administrivia.\n");
722  }
723 #endif
724 
725  if (helpp) {
726  printf("\n");
727  printf("Help [command]*\n");
728  printf("\t\t This command gives you information about racegen's\n");
729  printf("\t\t commands. If called with no arguments, it will print\n");
730  printf("\t\t information about all available commands. Otherwise it\n");
731  printf("\t\t only prints information about the specified commands.\n");
732  }
733 
734  if (load) {
735  printf("\n");
736  printf("Load [filename]\n");
737  printf("\t\t This command will load your race from the file specified\n");
738  printf("\t\t in the optional first argument, or from the file \"%s\"\n",
739  (race_info.filename[0] ? race_info.filename : SAVETO));
740  printf("\t\t if no argument is given.\n");
741  }
742 
743  if (modify) {
744  printf("\n");
745  printf("Modify arg1 arg2\n");
746  printf("\t\t This command allows you to change the values of your\n");
747  printf(
748  "\t\t race's name, password, type, attributes, planet, and compats.\n");
749  printf("\t\t The syntax is as follows:\n");
750  printf("\t\t <modify> ::= modify <attr> <value>\n");
751  printf("\t\t | modify address <string>\n");
752  printf("\t\t | modify name <string>\n");
753  printf("\t\t | modify password <string>\n");
754  printf("\t\t | modify planet <planettype>\n");
755 #ifdef PRIV
756  printf("\t\t | modify privilege <privtype>\n");
757 #endif
758  printf("\t\t | modify race <racetype>\n");
759  printf("\t\t | modify <sectortype> <value>\n");
760 
761  printf("\t\t <attribute> ::= %s", attr[0].print_name);
762  for (i = FIRST_ATTRIBUTE + 1; i < LAST_ATTRIBUTE; i++) {
763  printf(" | %s", attr[i].print_name);
764  if ((i % 3) == 2) printf("\n\t\t ");
765  }
766  printf("\n");
767 
768  printf("\t\t <planettype> ::= %s", planet_print_name[0]);
769  for (i = FIRST_HOME_PLANET_TYPE + 1;
770  i <= std::min(4, LAST_HOME_PLANET_TYPE); i++) {
771  printf(" | %s", planet_print_name[i]);
772  }
773  printf("\n\t\t ");
774  for (; i <= LAST_HOME_PLANET_TYPE; i++) {
775  printf(" | %s", planet_print_name[i]);
776  }
777  printf("\n");
778 
779  printf("\t\t <racetype> ::= %s", race_print_name[0]);
780  for (i = FIRST_RACE_TYPE + 1; i <= LAST_RACE_TYPE; i++) {
781  printf(" | %s", race_print_name[i]);
782  }
783  printf("\n");
784 
785  printf("\t\t <sectortype> ::= %s", sector_print_name[1]);
786  for (i = FIRST_SECTOR_TYPE + 2; i <= std::min(5, LAST_SECTOR_TYPE); i++) {
787  printf(" | %s", sector_print_name[i]);
788  }
789  printf("\n\t\t ");
790  for (; i <= LAST_SECTOR_TYPE; i++) {
791  printf(" | %s", sector_print_name[i]);
792  }
793  printf("\n");
794  }
795 
796  if (print) {
797  printf("\n");
798  printf("Print [filename]\n");
799  printf("\t\t With no argument, this command prints your race to the\n");
800  printf("\t\t screen. It is automatically executed after each modify.\n");
801  printf("\t\t Otherwise it saves a text copy of your race to the file\n");
802  printf("\t\t specified in the first argument.\n");
803  }
804 
805 #ifdef PRIV
806  if (process) {
807  printf("\n");
808  printf("Process filename\n");
809  printf("\t\t This command will repeatedly load races from filename,\n");
810  printf("\t\t and then try to enroll them. You can thus easily \n");
811  printf("\t\t enroll tens of players at once. \n");
812  }
813 #endif
814 
815  if (save) {
816  printf("\n");
817  printf("Save [filename]\n");
818  printf("\t\t This command will save your race to the file specified in\n");
819  printf("\t\t the optional first argument, or to the file \"%s\"\n",
820  (race_info.filename[0] ? race_info.filename : SAVETO));
821  printf("\t\t if no argument is given.\n");
822  }
823 
824  if (send2) {
825  printf("\n");
826  printf("Send\n");
827  printf("\t\t This command will send your race to God, (%s).\n", TO);
828  printf("\t\t It will not work unless you have spent all your points.\n");
829  }
830 
831  if (quit) {
832  printf("\n");
833  printf("Quit\n");
834  printf("\t\t This command will prompt you to save your work if you\n");
835  printf("\t\t haven't already, and then exit this program.\n");
836  }
837 
838  printf("\n");
839 }
840 
841 /*
842  * Return non-zero on failure, zero on success. */
843 int load_from_file(FILE *g) {
844  int i;
845  char buf[80];
846  char from_address[80];
847 
848 #define FSCANF(file, format, variable)
849  if (EOF == fscanf((file), (format), (variable))) goto premature_end_of_file
850 
851  do {
852  FSCANF(g, " %s", buf);
853  if (0 == strcmp(buf, "From:")) {
854  FSCANF(g, " %s", buf);
855  strcpy(from_address, buf);
856  }
857  } while (strcmp(buf, START_RECORD_STRING));
858 
859  race_info.status = STATUS_BALANCED;
860  FSCANF(g, " %s", race_info.address);
861  FSCANF(g, " %s", race_info.name);
862  FSCANF(g, " %s", race_info.password);
863  FSCANF(g, " %d", &race_info.priv_type);
864  FSCANF(g, " %d", &race_info.home_planet_type);
865  FSCANF(g, " %d", &race_info.race_type);
866  if (race_info.race_type == R_NORMAL)
867  normal();
868  else
869  metamorph();
870  for (i = FIRST_ATTRIBUTE; i <= LAST_ATTRIBUTE; i++)
871  FSCANF(g, " %lf", &race_info.attr[i]);
872  fix_up_iq();
873  for (i = FIRST_SECTOR_TYPE; i <= LAST_SECTOR_TYPE; i++)
874  FSCANF(g, " %lf", &race_info.compat[i]);
875  do {
876  FSCANF(g, " %s", buf);
877  } while (strcmp(buf, END_RECORD_STRING));
878  return 0;
879 premature_end_of_file:
880  printf("Error: premature end of file.\n");
881  return 1;
882 }
883 
884 /*
885  * Return non-zero on failure, zero on success. */
886 
887 static int load_from_filename(const char *filename) {
888  int ret;
889  FILE *f = fopen(filename, "r");
890 
891  if (f == nullptr) {
892  printf("Unable to open file \"%s\".\n", filename);
893  return 1;
894  }
895  ret = load_from_file(f);
896  fclose(f);
897  return ret;
898 }
899 
900 static void load(int argc, char *argv[]) {
901  char c[64];
902  int i;
903 
904  bcopy(&race_info, &last, sizeof(struct x));
905  if (altered) {
906  i = Dialogue("This race has been altered; load anyway?", "yes", "no", 0);
907  if (i == 1) return;
908  }
909  if (argc > 1)
910  strcpy(c, argv[1]);
911  else if (!race_info.filename[0])
912  strcpy(c, SAVETO);
913  if (load_from_filename(c)) {
914  printf("Load from file \"%s\" failed.\n", c);
915  bcopy(&last, &race_info, sizeof(struct x));
916  } else {
917  printf("Loaded race from file \"%s\".\n", c);
918  strcpy(race_info.filename, c);
919  altered = 0;
920  changed = 1;
921  }
922 }
923 
924 static int modify(int argc, char *argv[]) {
925  int i;
926  int j;
927  static char *help_strings[2] = {nullptr, "modify"};
928  double f;
929 
930  if (argc < 3) {
931  help(2, help_strings);
932  return -1;
933  }
934  j = strlen(argv[1]);
935 
936  bcopy(&race_info, &last, sizeof(struct x));
937 
938  /*
939  * Check for attribute modification: */
940  for (i = FIRST_ATTRIBUTE; i <= LAST_ATTRIBUTE; i++)
941  if (!strncasecmp(argv[1], attr[i].print_name, j)) {
942  if (attr[i].is_integral == 2) { /* Boolean attribute. */
943  j = strlen(argv[2]);
944  if (!strncasecmp(argv[2], "no", j))
945  f = 0.0;
946  else if (!strncasecmp(argv[2], "yes", j))
947  f = 1.0;
948  else
949  f = atof(argv[2]);
950  } else
951  f = atof(argv[2]);
952 
953  race_info.attr[i] = f;
954  fix_up_iq();
956  }
957 
958  /*
959  * Check for name modification: */
960  if (!strncasecmp(argv[1], "name", j)) {
961  strcpy(race_info.name, argv[2]);
963  }
964 
965  /*
966  * Check for from-address modification: */
967  if (!strncasecmp(argv[1], "address", j)) {
968  strcpy(race_info.address, argv[2]);
970  }
971 
972 #ifdef PRIV
973  /*
974  * Check for privilege modification: */
975  if (!strncasecmp(argv[1], "privilege", j)) {
976  j = strlen(argv[2]);
977  for (i = FIRST_PRIV_TYPE; i <= LAST_PRIV_TYPE; i++)
978  if (!strncasecmp(argv[2], priv_print_name[i], j)) {
979  race_info.priv_type = i;
980  return critique_modification();
981  }
982  race_info.priv_type = atof(argv[2]);
983  return critique_modification();
984  }
985 #endif
986 
987  /*
988  * Check for planet modification: */
989  if (!strncasecmp(argv[1], "planet", j)) {
990  j = strlen(argv[2]);
991  for (i = FIRST_HOME_PLANET_TYPE; i <= LAST_HOME_PLANET_TYPE; i++)
992  if (!strncasecmp(argv[2], planet_print_name[i], j)) {
993  if (i == H_JOVIAN) {
994  bzero(race_info.compat, sizeof(race_info.compat));
995  race_info.compat[S_GAS] = 100.0;
996  } else if (race_info.home_planet_type == H_JOVIAN) {
997  race_info.compat[S_PLATED] = 100.0;
998  race_info.compat[S_GAS] = 0.0;
999  }
1000  race_info.home_planet_type = i;
1001  return critique_modification();
1002  }
1003  printf("\"%s\" is not a valid planet type.\n", argv[2]);
1004  return -1;
1005  }
1006 
1007  /*
1008  * Check for password modification: */
1009  if (!strncasecmp(argv[1], "password", j)) {
1010  strcpy(race_info.password, argv[2]);
1011  return critique_modification();
1012  }
1013 
1014  /*
1015  * Check for race modification: */
1016  if (!strncasecmp(argv[1], "race", j)) {
1017  j = strlen(argv[2]);
1018  for (i = FIRST_RACE_TYPE; i <= LAST_RACE_TYPE; i++)
1019  if (!strncasecmp(argv[2], race_print_name[i], j)) {
1020  if (i == R_METAMORPH) {
1021  race_info.attr[ABSORB] = 1;
1022  race_info.attr[PODS] = 1;
1023  race_info.attr[COL_IQ] = 1;
1024  metamorph();
1025  } else {
1026  race_info.attr[ABSORB] = 0;
1027  race_info.attr[PODS] = 0;
1028  race_info.attr[COL_IQ] = 0;
1029  normal();
1030  }
1031  race_info.race_type = i;
1032  return critique_modification();
1033  }
1034  printf("\"%s\" is not a valid race type.\n", argv[2]);
1035  return -1;
1036  }
1037 
1038  /*
1039  * Check for sector_type modification: */
1040  for (i = FIRST_SECTOR_TYPE; i <= LAST_SECTOR_TYPE; i++)
1041 
1042 #ifndef PRIV
1043  if (i == S_PLATED)
1044  continue; /* Players should never need to modify this. */
1045  else
1046 #endif
1047  if (!strncasecmp(argv[1], sector_print_name[i], j)) {
1048  race_info.compat[i] = atof(argv[2]);
1049  return critique_modification();
1050  }
1051 
1052  /*
1053  * Print error */
1054  printf("\n");
1055  printf("Modify: didn't recognize the first argument \"%s\".\n", argv[1]);
1056  printf("Type \"help modify\" for more information on modify.\n");
1057  printf("\n");
1058  return -1;
1059 }
1060 
1061 void print_to_file(FILE *f, int verbose) {
1062 #define FPRINTF
1063  if (verbose) fprintf
1064  int i;
1065 
1066  if (!verbose) fprintf(f, START_RECORD_STRING);
1067 
1068  FPRINTF(f, "\nAddress :");
1069  fprintf(f, " %s", race_info.address);
1070  FPRINTF(f, "\n");
1071 
1072  FPRINTF(f, "Name :");
1073  fprintf(f, " %s", race_info.name);
1074  FPRINTF(f, "\n");
1075 
1076  FPRINTF(f, "Password :");
1077  fprintf(f, " %s", race_info.password);
1078  FPRINTF(f, "\n");
1079 
1080 #ifdef PRIV
1081  FPRINTF(f, "Privileges:");
1082  if (verbose)
1083  fprintf(f, "%11.11s", priv_print_name[race_info.priv_type]);
1084  else
1085 #endif
1086  if (!verbose)
1087  fprintf(f, " %d", race_info.priv_type);
1088 
1089 #ifdef PRIV
1090  FPRINTF(f, "\n");
1091 #endif
1092 
1093  FPRINTF(f, "Planet :");
1094  if (verbose)
1095  fprintf(f, " %s", planet_print_name[race_info.home_planet_type]);
1096  else
1097  fprintf(f, " %d", race_info.home_planet_type);
1098  FPRINTF(f, " [%4d]\n", cost_info.home_planet_type);
1099 
1100  FPRINTF(f, "Race type:");
1101  if (verbose)
1102  fprintf(f, " %s", race_print_name[race_info.race_type]);
1103  else
1104  fprintf(f, " %d", race_info.race_type);
1105  FPRINTF(f, " [%4d]\n", cost_info.race_type);
1106  FPRINTF(f, "\n");
1107 
1108  FPRINTF(f, "Attributes:\n");
1109  for (i = FIRST_ATTRIBUTE; i <= LAST_ATTRIBUTE; i++) {
1110  FPRINTF(f, "%13.13s:", attr[i].print_name);
1111  if (verbose && (attr[i].is_integral == 2))
1112  fprintf(f, (race_info.attr[i] > 0.0) ? " yes " : " no ");
1113  else
1114  fprintf(f, " %7.2f", race_info.attr[i]);
1115  FPRINTF(f, " [%4.0f]", cost_info.attr[i]);
1116  FPRINTF(f, (i & 01) ? "\n" : " ");
1117  }
1118  if (i & 01) FPRINTF(f, "\n");
1119  FPRINTF(f, "\n");
1120 
1121  FPRINTF(f, "Sector Types: %2d [%4d]\n", race_info.n_sector_types,
1122  cost_info.n_sector_types);
1123  for (i = FIRST_SECTOR_TYPE; i <= LAST_SECTOR_TYPE; i++) {
1124  FPRINTF(f, "%13.13s: ", sector_print_name[i]);
1125  fprintf(f, " %3.0f", race_info.compat[i]);
1126  FPRINTF(
1127  f, "%% %c[%4.0f]",
1128  (planet_compat_cov[race_info.home_planet_type][i] == 1.0) ? '*' : ' ',
1129  cost_info.compat[i]);
1130  FPRINTF(f, (i & 01) ? "\n" : " ");
1131  }
1132 
1133  if (!verbose) fprintf(f, END_RECORD_STRING);
1134  FPRINTF(f, "\n");
1135  FPRINTF(f, "Points left: %d Previous value: %d\n", npoints,
1136  last_npoints);
1137  fprintf(f, "\n");
1138 #undef FPRINTF
1139 }
1140 
1141 static int print_to_filename(const char *filename, int verbose) {
1142  FILE *f = fopen(filename, "w");
1143 
1144  if (f == nullptr) {
1145  printf("Unable to open file \"%s\".\n", filename);
1146  return 0;
1147  }
1148  print_to_file(f, verbose);
1149  fclose(f);
1150  return 1;
1151 }
1152 
1153 static void print(int argc, char *argv[]) {
1154  if (argc == 1)
1155  print_to_file(stdout, 1);
1156  else if (print_to_filename(argv[1], 1))
1157  printf("Printed race to file \"%s\".\n", argv[1]);
1158 }
1159 
1160 static void save(int argc, char *argv[]) {
1161  if (argc > 1)
1162  strcpy(race_info.filename, argv[1]);
1163  else if (!race_info.filename[0])
1164  strcpy(race_info.filename, SAVETO);
1165  if (print_to_filename(race_info.filename, 0)) {
1166  printf("Saved race to file \"%s\".\n", race_info.filename);
1167  altered = 0;
1168  }
1169 }
1170 
1171 static void send2(int, char **) {
1172  FILE *f;
1173  char sys[64];
1174 
1175  bcopy(&race_info, &last, sizeof(struct x));
1176  if (critique_to_file(stdout, 1, IS_PLAYER)) return;
1177 
1178  f = fopen(race_info.password, "w");
1179  if (f == nullptr) {
1180  printf("Unable to open file \"%s\".\n", race_info.password);
1181  return;
1182  }
1183  fprintf(f, "From: %s\n", race_info.address);
1184  fprintf(f, "Subject: %s Race Submission\n", GAME);
1185  fprintf(f, "\n");
1186  print_to_file(f, 0);
1187  fclose(f);
1188 
1189  fflush(stdout);
1190  printf("Mailing race to %s : ", TO);
1191  sprintf(sys, "cat %s | %s %s", race_info.password, MAILER, TO);
1192  system(sys);
1193  printf("done.\n");
1194 
1195  f = fopen(race_info.password, "w");
1196  if (f == nullptr) {
1197  printf("Unable to open file \"%s\".\n", race_info.password);
1198  return;
1199  }
1200  fprintf(f, "From: %s\n", race_info.address);
1201  fprintf(f, "Subject: %s Race Submission\n\n", GAME);
1202  print_to_file(f, 1);
1203  fclose(f);
1204 
1205  fflush(stdout);
1206  printf("Mailing race to %s : ", race_info.address);
1207  sprintf(sys, "cat %s | %s %s", race_info.password, MAILER, race_info.address);
1208  system(sys);
1209  printf("done.\n");
1210  unlink(race_info.password);
1211 }
1212 
1213 int Dialogue(const char *prompt, ...) {
1214  va_list ap;
1215 #define INPUTSIZE 512
1216  char input[INPUTSIZE];
1217  char *carg;
1218  int len;
1219  int i;
1220  int argc = 0;
1221  int init = 0;
1222  char *argv[16];
1223  printf("%s", prompt);
1224  va_start(ap, prompt);
1225  while ((carg = va_arg(ap, char *)) != nullptr) {
1226  if (!init) {
1227  printf(" [%s", carg);
1228  init = 1;
1229  } else {
1230  printf("/%s", carg);
1231  }
1232  argv[argc++] = carg++;
1233  }
1234  va_end(ap);
1235  if (argc) printf("]");
1236  printf("> ");
1237  fflush(stdout);
1238  while (true) {
1239  fgets(input, INPUTSIZE, stdin);
1240 
1241  if (argc == 0) return -1;
1242  len = strlen(input) - 1;
1243 
1244  for (i = 0; i < argc; i++)
1245  if (!strncasecmp(argv[i], input, len)) return i;
1246  /*
1247  * The input did not match any of the valid responses: */
1248  printf("Please enter ");
1249  for (i = 0; i < argc - 1; i++) {
1250  printf("\"%s\", ", argv[i]);
1251  }
1252  printf("or \"%s\"> ", argv[i]);
1253  }
1254 }
1255 
1256 static void quit(int, char **) {
1257  int i;
1258 
1259  if (please_quit) { /* This could happen if ^c is hit while here. */
1260  if (isserver) close(fd);
1261  exit(0);
1262  }
1263  please_quit = 1;
1264  if (altered) {
1265  if (!isserver) {
1266  i = Dialogue("Save this race before quitting?", "yes", "no", "abort", 0);
1267  if (i == 0)
1268  save(1, nullptr);
1269  else if (i == 2)
1270  please_quit = 0;
1271  } else {
1272  i = Dialogue("Are you sure?", "yes", "no", "abort", 0);
1273  if (i == 1) please_quit = 0;
1274  }
1275  }
1276 }
1277 
1278 /**************
1279  * This function merely takes the space-parsed command line and executes
1280  * one of the commands above.
1281  */
1282 static void execute(int argc, char **argv) {
1283  int i;
1284 
1285 #if 0
1286  for (i = 0; i < argc; i++)
1287  printf ("%d: \"%s\"\n", i, argv[i]);
1288 #endif
1289  if (argc == 0) {
1290  printf("Type \"help\" for help.\n");
1291  return;
1292  }
1293  i = strlen(argv[0]);
1294 #ifdef PRIV
1295  if (!strncasecmp(argv[0], "enroll", i) && !isserver)
1296  enroll(argc, argv);
1297  else
1298 #endif
1299  if (!strncasecmp(argv[0], "help", i))
1300  help(argc, argv);
1301  else if (!strncasecmp(argv[0], "load", i) && !isserver)
1302  load(argc, argv);
1303  else if (!strncasecmp(argv[0], "modify", i))
1304  modify(argc, argv);
1305  else if (!strncasecmp(argv[0], "print", i))
1306  print(argc, argv);
1307 #ifdef PRIV
1308  else if (!strncasecmp(argv[0], "process", i) && !isserver)
1309  process(argc, argv);
1310 #endif
1311  else if (!strncasecmp(argv[0], "save", i) && !isserver)
1312  save(argc, argv);
1313  else if (!strncasecmp(argv[0], "send", i))
1314  send2(argc, argv);
1315  else if (!strncasecmp(argv[0], "quit", i))
1316  quit(argc, argv);
1317  else {
1318  printf("Unknown command \"%s\". Type \"help\" for help.\n", argv[0]);
1319  return;
1320  }
1321 }
1322 
1323 /**************
1324  * Here is the main loop, where I print the command prompt, parse it into
1325  * words using spaces as the separator, and call execute. Level is the
1326  * number of higher level modify print loops above this one. It will
1327  * always be zero for player racegens.
1328  */
1330 #define BUFSIZE 512
1331  char buf[BUFSIZE];
1332  char *com;
1333  char *args[4];
1334  int i;
1335 
1336  while (!please_quit) {
1339 
1340  if (changed) {
1341  print_to_file(stdout, 1);
1342  changed = 0;
1343  }
1344 #ifdef PRIV
1345  if (isserver)
1346  printf("Command [help/modify/print/send/quit]> ");
1347  else
1348  printf("%s [enroll/help/load/modify/print/process/save/send/quit]> ",
1349  level ? "Fix" : "Command");
1350 #else
1351  printf("Command [help/load/modify/print/save/send/quit]> ");
1352 #endif
1353  fflush(stdout);
1354  com = fgets(buf, BUFSIZE, stdin);
1355  buf[strlen(buf) - 1] = '\0';
1356 
1357  for (i = 0; i < 4; i++) {
1358  while (*com && (*com == ' ')) *com++ = '\0';
1359  if (!*com) break;
1360  args[i] = com;
1361  while (*com && (*com != ' ')) com++;
1362  }
1363  execute(i, args);
1364  }
1365  printf("\n");
1366 }
1367 
1368 /**************
1369  * Print out initial info and then call modify-print loop.
1370  */
1371 static int do_racegen() {
1372  initialize();
1373  printf("\n");
1374  printf("Galactic Bloodshed Race Generator %s\n", VERSION);
1375  printf("\n");
1376  printf("Finished races will be sent to %s.\n", TO);
1377  printf("***************************************************************\n");
1378  printf("Game: %s, using %s\n", GAME, GB_VERSION);
1379  printf("Address: %s\n", LOCATION);
1380  printf("God: %s\n", MODERATOR);
1381  printf("Starts: %s\n", STARTS);
1382  printf("Stars: %s; Players: %s\n", STARS, PLAYERS);
1383  printf("Any race may be refused or modified by God for any reason!\n");
1384  printf("\n");
1385  printf("DEADLINE: %s\n", DEADLINE);
1386  printf("***************************************************************\n");
1387  printf("Update schedule:\n");
1388  printf("%s\n", UPDATE_SCH);
1389  printf("\n");
1390  printf("If you cannot make this update schedule, do NOT send in a race!\n");
1391  printf("***************************************************************\n");
1392  printf(OTHER_STUFF);
1393  printf("\n");
1394  fflush(stdout);
1395  Dialogue("Hit return", 0);
1396 
1398 
1399  return 0;
1400 }
static attribute attr[N_ATTRIBUTES]
Definition: racegen.cc:116
#define MAILER
Definition: racegen.h:29
static int isserver
Definition: racegen.cc:29
#define ADVENT
Definition: racegen.h:45
static void execute(int argc, char **argv)
Definition: racegen.cc:1282
#define H_JOVIAN
Definition: racegen.h:110
int altered
Definition: racegen.cc:323
Definition: racegen.h:175
static void print(int argc, char *argv[])
Definition: racegen.cc:1153
#define MASS
Definition: racegen.h:53
#define ATTR_RANGE(a)
Definition: racegen.h:68
int critique_to_file(FILE *f, int rigorous_checking, int is_player_race)
Definition: racegen.cc:440
int please_quit
Definition: racegen.cc:325
static int modify(int argc, char *argv[])
Definition: racegen.cc:924
static void save(int argc, char *argv[])
Definition: racegen.cc:1160
#define LOCATION
Definition: game_info.h:16
int priv_type
Definition: racegen.h:185
double cov[N_ATTRIBUTES]
Definition: racegen.h:64
#define FIRST_PRIV_TYPE
Definition: racegen.h:132
#define STARTING_POINTS
Definition: game_info.h:33
#define LAST_ATTRIBUTE
Definition: racegen.h:56
#define STATUS_UNBALANCED
Definition: racegen.h:169
#define MODERATOR
Definition: game_info.h:17
double compat[N_SECTOR_TYPES]
Definition: racegen.h:188
#define LAST_RACE_TYPE
Definition: racegen.h:123
#define S_PLATED
Definition: racegen.h:152
#define ROUND(f)
#define FPRINTF
struct x race_info cost_info last
Definition: racegen.cc:319
const double compat_cov[N_SECTOR_TYPES][N_SECTOR_TYPES]
Definition: racegen.cc:264
static int print_to_filename(const char *filename, int verbose)
Definition: racegen.cc:1141
static void help(int, char *[])
Definition: racegen.cc:661
#define N_SECTOR_TYPES
Definition: racegen.h:154
const int planet_cost[N_HOME_PLANET_TYPES]
Definition: racegen.cc:252
static void initialize()
Definition: racegen.cc:631
double attr[N_ATTRIBUTES]
Definition: racegen.h:183
#define PODS
Definition: racegen.h:52
#define P_NORMAL
Definition: racegen.h:135
#define GB_VERSION
Definition: game_info.h:15
const char * priv_print_name[N_PRIV_TYPES]
Definition: racegen.cc:257
#define H_EARTH
Definition: racegen.h:104
int cost_of_race()
Definition: racegen.cc:332
#define N_RACE_TYPES
Definition: racegen.h:124
double e_fudge
Definition: racegen.h:62
#define IS_PLAYER
Definition: racegen.h:23
int load_from_file(FILE *g)
Definition: racegen.cc:843
#define END_RECORD_STRING
Definition: racegen.h:37
char status
Definition: racegen.h:181
char address[64]
Definition: racegen.h:176
const int race_cost[N_RACE_TYPES]
Definition: racegen.cc:255
#define FIRST_RACE_TYPE
Definition: racegen.h:120
#define FIRST_SECTOR_TYPE
Definition: racegen.h:144
#define FIRST_HOME_PLANET_TYPE
Definition: racegen.h:103
int race_type
Definition: racegen.h:184
char print_name[16]
Definition: racegen.h:61
double init
Definition: racegen.h:63
static void send2(int, char **)
Definition: racegen.cc:1171
int main()
Definition: racegen.cc:45
#define FSCANF(file, format, variable)
#define COL_IQ
Definition: racegen.h:48
void print_to_file(FILE *f, int verbose)
Definition: racegen.cc:1061
#define INPUTSIZE
double minimum
Definition: racegen.h:63
#define A_IQ
Definition: racegen.h:50
#define MIN_PASSWORD_LENGTH
Definition: game_info.h:34
const int blah[N_SECTOR_TYPES]
Definition: racegen.cc:262
int last_npoints
Definition: racegen.cc:322
static int load_from_filename(const char *filename)
Definition: racegen.cc:887
#define N_PRIV_TYPES
Definition: racegen.h:137
const char * planet_print_name[N_HOME_PLANET_TYPES]
Definition: racegen.cc:250
#define LAST_PRIV_TYPE
Definition: racegen.h:136
static int do_racegen()
Definition: racegen.cc:1371
static void normal()
Definition: racegen.cc:401
#define SEXES
Definition: racegen.h:54
#define SAVETO
Definition: racegen.h:30
double e_factor
Definition: racegen.h:62
#define R_NORMAL
Definition: racegen.h:121
int Dialogue(const char *,...)
Definition: racegen.cc:1213
const double planet_compat_cov[N_HOME_PLANET_TYPES][N_SECTOR_TYPES]
Definition: racegen.cc:299
#define FIRST_ATTRIBUTE
Definition: racegen.h:44
static int critique_modification()
Definition: racegen.cc:614
#define LAST_SECTOR_TYPE
Definition: racegen.h:153
#define BUFSIZE
#define N_ATTRIBUTES
Definition: racegen.h:57
#define S_GAS
Definition: racegen.h:148
#define PLAYERS
Definition: game_info.h:18
#define START_RECORD_STRING
Definition: racegen.h:36
#define GAME
Definition: game_info.h:14
#define OTHER_STUFF
Definition: game_info.h:25
int is_integral
Definition: racegen.h:65
int changed
Definition: racegen.cc:324
const char * sector_print_name[N_SECTOR_TYPES]
Definition: racegen.cc:259
char password[64]
Definition: racegen.h:179
#define STATUS_BALANCED
Definition: racegen.h:170
double l_fudge
Definition: racegen.h:62
int home_planet_type
Definition: racegen.h:186
double l_factor
Definition: racegen.h:62
#define VERSION
Definition: racegen.h:14
#define N_HOME_PLANET_TYPES
Definition: racegen.h:112
#define ABSORB
Definition: racegen.h:46
int n_sector_types
Definition: racegen.h:187
static void load(int, char *[])
Definition: racegen.cc:900
int npoints
Definition: racegen.cc:321
#define METAB
Definition: racegen.h:55
void modify_print_loop(int level)
Definition: racegen.cc:1329
#define STARS
Definition: game_info.h:19
static void fix_up_iq()
Definition: racegen.cc:425
static void metamorph()
Definition: racegen.cc:374
#define R_METAMORPH
Definition: racegen.h:122
static int fd
Definition: racegen.cc:28
char rejection[256]
Definition: racegen.h:180
char name[64]
Definition: racegen.h:178
#define FERT
Definition: racegen.h:49
static void quit(int argc, char **argv)
Definition: racegen.cc:1256
#define UPDATE_SCH
Definition: game_info.h:22
double e_hinge
Definition: racegen.h:62
#define DEADLINE
Definition: game_info.h:13
#define LAST_HOME_PLANET_TYPE
Definition: racegen.h:111
#define BIRTH
Definition: racegen.h:47
double maximum
Definition: racegen.h:63
#define FIGHT
Definition: racegen.h:51
#define TO
Definition: game_info.h:21
char filename[64]
Definition: racegen.h:177
const char * race_print_name[N_RACE_TYPES]
Definition: racegen.cc:254
#define STARTS
Definition: game_info.h:20