🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

1 total messages Started by Fiziks Wed, 27 Jan 1993 11:57
racegen.c
#465
Author: Fiziks
Date: Wed, 27 Jan 1993 11:57
1346 lines
41619 bytes
/* racegen - a Galactic Bloodshed race creation program.
 * Copyright (c) Leonard Dickens 1991   (leonard@cs.umd.edu)
 *
 * Permission to copy, distribute, and/or alter is granted as long as the copy-
 * right notice and these terms are left unchanged in all derivatives/copies.
 * 
 * Anybody who does alter this program, please take credit!
 */

#include <varargs.h>

/* Slightly hacked version: altered to use varargs by Tom Boutell. */

/*
 * History:
 * 09-13-91 1.0.0 LD Started work
 * 09-14-91 1.0.0 LD Finished except for tweaking cost parameters.
 * 09-15-91 1.0.0 LD Added linear parameterizations for all attributes.
 *   Added exponential portions to most of the attributes.  Structured 
 *   attributes to allow easy expansion.
 * 09-15-91 1.1.0 LD Added load and save (non-verbose), and print-to-file.
 * 09-16-91 1.2.0 LD Added sector cost-covariances.
 * 09-16-91 1.2.1 LD Added attribute cost-covariances.  Tweaked n_sector costs.
 * 09-17-91       LD Posted the program.  Let's see what the net digs up.
 * 09-18-91 1.2.2 LD Tweaked help to not display plated as a sector option.
 *   Made gas races unable to have any other sector compat.  Made default
 *   IQ be only 125.  Made max birthrate be 1.0.
 * 09-19-91 1.2.3 LD More tweaks to get attribute costs like I want them.
 * 09-20-91 1.2.4 LD Added new #defines and prints for more game-dependent
 *   information for the players' edification.
 * 09-20-91 1.3.0 LD Added all morph attributes separately; removed race costs.
 * 09-25-91 1.4.0 LD Added ENROLL variant code.
 * 09-25-91       LD Posted the program again.  
 * 09-30-91 1.4.1 LD Yeck, this is more complicated than I thought.  Sigh.
 *   Added an address field to the race structure.  Separated critique info
 *   out from modify to allow it to be called from enroll.  Added "last"
 *   race in order to always have a valid race to fall back on.  Added
 *   rejection field to x structure for enroll to use.
 * 09-31-91 1.4.2 LD Added cost extra cost for non-typical sectors.  This
 *   if in essense just a planet_type-compat covariance.
 * 10-01-91 1.4.3 LD Separated out modify-print loop in order to allow
 *   enroll to recursively edit.
 * 10-03-91 1.4.4 LD Mods to compile with old GB enroll.  Changed "IQ" to
 *   something else...A_IQ.  Changed OTHER to OTHER_STUFF.  Changed order
 *   of sector defines to match DES_ defines in tweakables.h
 * 10-03-91 1.5.0 LD File breakup to promote separate compilation.
 * 10-04-91 1.5.1 LD Added Dialogue command to centralize simple exchanges.
 *   this is really just a CS major nicety; it is not needed.
 * 10-06-91 1.5.2 LD Fixed bug in critique that squelched jovian compat.
 * 10-07-91 1.5.3 LD Fixed iq/iq_limit load covariance problem.
 * 12-15-92 1.5.3 JPD Added 1.5.3 to GB+ distribution.  Added racegen.c to
 *   utils directory, racegen.h to hdrs directory, and docs to docs direc.
 * 
 *
 * Thanks to:
 * Clay Luther for the original, (perl) racegen.  I made sure to look at it
 *   before starting this, and in got much of the linear aspects of my
 *   attribute cost functions from his program.  Also, the 125 IQ is Clay's.
 * Keesh, for the numbers, too.  At least, Clay thanks him for them, so I
 *   think I should too.
 * Paul A Daniels for pointing out that help displayed plated as a possible
 *   argument for modify.
 * jtop@cs.kun.nl for the race with gas and other compats too.  My blind spot.
 * Doug Ingram for suggesting more game-dependent info be included.
 *
 * Version: (sccs) 1.2 18:26:46
 */
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>



/**************
 * Definitions for data types and such used in the program.
 */
#include "racegen.h"


attribute attr[N_ATTRIBUTES] = {
  {ADVENT, "Adventurism",
     0.0, 0.0, 0.0, 300.0, 0.0,
     0.05, 0.4, 0.99,
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     0},
  {ABSORB, "Absorb",
     0.0, 0.0, 0.0, 150.0, 0.0,
     0.00, 0.00, 1.00,
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     2},
  {BIRTH, "Birthrate", 
     0.0, 0.0, 0.0, 500.0, 0.0,
     0.2, 0.6, 1.0, 
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     0},
  {COL_IQ, "Collective IQ",
     0.0, 0.0, 0.0, -350.5, 0.0,
     0.00, 0.00, 1.00,
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     2},
  {FERT, "Fertilize", 
     200.0, 1.0, 1.0, 300.0, 0.0,
     0.0, 0.0, 1.0, 
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     0},
  {A_IQ, "IQ", 
     100.0, 0.03, 140, 6, 0.0,
     50, 125, 220,
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     1},
  {FIGHT, "Fight", 
     10.0, 0.4, 6.0, 65.0, 0.0,
     1, 4, 20, 
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     1},
  {PODS, "Pods",
     0.0, 0.0, 0.0, 250.0, 0.0,
     0.00, 0.00, 1.00,
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     2},
  {MASS, "Mass", 
     100.0, 1.0, 3.1, -100.0, 0.0,
     0.1, 1.0, 3.0, 
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     0},
  {SEXES, "Sexes", 
     2.2, -0.5, 9.0, -3.0, 0.0, 
     1, 2, 53, 
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     1},
  {METAB, "Metabolism", 
     300.0, 0.8, 1.3, 700.0, 0.0,
     0.1, 0.8, 4.0, 
     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
     0}
  } ;

const char *planet_print_name[N_HOME_PLANET_TYPES] = {
"Earth", "Forest", "Desert", "Water", "Airless", "Iceball", "Jovian"} ;
const int planet_cost[N_HOME_PLANET_TYPES] = {
75, 50, 50, 50, -25, -25, 700} ;

const char *race_print_name[N_RACE_TYPES] = {"Normal", "Metamorph"} ;
const int race_cost[N_RACE_TYPES] = {0, 0} ;

const char *priv_print_name[N_PRIV_TYPES] = {"God", "Guest", "Normal"} ;

const char *sector_print_name[N_SECTOR_TYPES] = {
"Water", "Land", "Mountain", "Gas", "Ice", "Forest", "Desert", "Plated"} ;

const int n_sector_types_cost[N_SECTOR_TYPES] = {
-1, 0, 0, 100, 160, 240, 280, 320} ;

const double compat_cov[N_SECTOR_TYPES][N_SECTOR_TYPES] = {
/*  .       *        ^       ~       #       )       -      o  */
  {0.0,    },
  { .001,  0.0      },
  { .002,  -.0005,  0.0,    },
  {999,    999,     999,    0.0,    },
  { .001,  0.0,     -.002,  999,    0.0,    },
  {0.0,    -.001,   0.0,    999,     .001,  0.0,    },
  { .003,  -.0005,  0.0,    999,    0.0,     .001,  0.0},
  {0.0,    0.0,     0.0,    999,    0.0,    0.0,    0.0,   0.0}
  } ;

const double planet_compat_cov[N_HOME_PLANET_TYPES][N_SECTOR_TYPES] = {
         /*  .      *      ^      ~      #      )      -      o  */
/* @ */  { 1.00,  1.00,  1.50, 99.00,  1.3,  .8,  1.50,  1.01},
/* ) */  { 1.01,  1.01,  1.50, 99.00,  1.3,  .8,  3.00,  1.01},
/* - */  { 3.00,  1.01,  1.50, 99.00,  1.3,  2.40,  1.00,  1.01},
/* . */  { 1.00,  1.01,  1.50, 99.00,  1.3,  .85,  3.00,  1.01},
/* O */  { 3.00,  1.00,  1.50, 99.00,  2.9,  2.40,  1.01,  1.01},
/* # */  { 3.00,  2.00,  1.00, 99.00,  1.3,  2.40,  2.00,  1.01},
/* ~ */  {99.00, 99.00, 99.00,  1.00, 99.00, 99.00, 99.00, 99.00}
	 } ;


/**************
 * Global variables for this program.
 */
struct x race, cost, last ;

int npoints = STARTING_POINTS ;
int last_npoints = STARTING_POINTS ;
int altered = 0 ;       /* 1 iff race has been altered since last saved */
int changed = 1 ;       /* 1 iff race has been changed since last printed */
int please_quit = 0 ;   /* 1 iff you want to exit ASAP. */



/**************
 * Price function for racegen.  Finds the cost of `race', and returns it.
 * As a side effect the costs for all the individual fields of `race' get
 * stuffed into `cost' for later printing.
 */
int cost_of_race()
{
  int i, j, sum = 0 ;

#define ROUND(f) ((int) (0.5 + (f)))
  for (i=FIRST_ATTRIBUTE; i<=LAST_ATTRIBUTE; i++)
    cost.attr[i] = (exp(attr[i].e_fudge * (race.attr[i] - attr[i].e_hinge)) 
		    * attr[i].e_factor + race.attr[i] * attr[i].l_factor) ; 
#ifdef COVARIANCES
  for (i=FIRST_ATTRIBUTE; i<=LAST_ATTRIBUTE; i++)
    for (j=FIRST_ATTRIBUTE; j<=LAST_ATTRIBUTE; j++)
      if (attr[i].cov[j] != 0.0)
	cost.attr[i] *= (1.0 + attr[i].cov[j] * race.attr[j]) ;
#endif
  for (i=FIRST_ATTRIBUTE; i<=LAST_ATTRIBUTE; i++)
    sum += (cost.attr[i] = ROUND(cost.attr[i] + attr[i].l_fudge)) ;
		  
  sum += (cost.home_planet_type = planet_cost[race.home_planet_type]) ;
  sum += (cost.race_type = race_cost[race.race_type]) ;
  race.n_sector_types = 0 ;
  for (i=FIRST_SECTOR_TYPE; i<=LAST_SECTOR_TYPE; i++) {
    if (race.compat[i] != 0.0)
      race.n_sector_types += 1 ;
    /* Get the base costs: */
    cost.compat[i] = race.compat[i]*0.5 + 10.8*log(1.0+race.compat[i]) ;
    }
#ifdef COVARIANCES
  for (i=FIRST_SECTOR_TYPE; i<=LAST_SECTOR_TYPE; i++)
    for (j=i+1; j<=LAST_SECTOR_TYPE; j++)
      if (compat_cov[j][i] != 0.0) {
	cost.compat[i] *= (1.0 + compat_cov[j][i] * race.compat[j]) ;
	cost.compat[j] *= (1.0 + compat_cov[j][i] * race.compat[i]) ;
	}
  for (i=FIRST_SECTOR_TYPE; i<=LAST_SECTOR_TYPE; i++)
    if (planet_compat_cov[race.home_planet_type][i] > 1.01)
      cost.compat[i] *= planet_compat_cov[race.home_planet_type][i] ;
#endif
  for (i=FIRST_SECTOR_TYPE; i<=LAST_SECTOR_TYPE; i++)
    sum += (cost.compat[i] = ROUND(cost.compat[i])) ;
  sum += (cost.n_sector_types = n_sector_types_cost[race.n_sector_types]) ;
  return sum ;
  }


void metamorph()
{
  /* Adventurousness is not correlated with Max IQ. */
  attr[ADVENT].cov[A_IQ]    =  0.00 ;
  /* A high birthrate is easier if few sexes, high metab, and low mass.*/
  /** Morphs are not born, they hatch.  THus the mass limitation is smaller:**/
  attr[BIRTH].cov[MASS]   =  0.10 / ATTR_RANGE(MASS) ;
  attr[BIRTH].cov[SEXES]  =  0.90 / ATTR_RANGE(SEXES) ;
  attr[BIRTH].cov[METAB]  = -0.20 / ATTR_RANGE(METAB) ;
  /* Natural fertilization is ultimately associated with metabolic activity */ 
  /** Morphs are naturally adept at fertilization. **/
  attr[FERT].cov[METAB]   = -0.60 / ATTR_RANGE(METAB) ;
  /* Fighting is easier for independent, high-mass, high-metab races:   */
  attr[FIGHT].cov[ADVENT] = -0.05 / ATTR_RANGE(ADVENT) ;
  attr[FIGHT].cov[MASS]   = -0.20 / ATTR_RANGE(MASS) ;
  attr[FIGHT].cov[METAB]  = -0.05 / ATTR_RANGE(METAB) ;
  /* A high metabolism is hard to support if you have high mass: */
  /** Due to general squishiness, this effect is not as strong for mesos. **/
  attr[METAB].cov[MASS]   =  0.05 / ATTR_RANGE(MASS) ;
  /* However, a large IQ is easier with high mass; (lot of brain space): */
  /** IQ represents max IQ, thus, no go. **/
  attr[A_IQ].cov[MASS]      =  0.00 ;
  /* IQ is cheaper by virtue of collective intelligence: */
  attr[A_IQ].cov[COL_IQ]    =  -0.4 / ATTR_RANGE(COL_IQ) ;

  strcpy(attr[A_IQ].print_name, "IQ Limit") ;
  }

void normal()
{
  /* Adventurousness is more likely with people smart enough to do it. */
  attr[ADVENT].cov[A_IQ]  = -0.10 / ATTR_RANGE(A_IQ) ;
  /* A high birthrate is easier if few sexes, high metab, and low mass.*/
  attr[BIRTH].cov[MASS]   =  0.40 / ATTR_RANGE(MASS) ;
  attr[BIRTH].cov[SEXES]  =  0.90 / ATTR_RANGE(SEXES) ;
  attr[BIRTH].cov[METAB]  = -0.20 / ATTR_RANGE(METAB) ;
  /* Natural fertilization is ultimately associated with metabolic activity */ 
  attr[FERT].cov[METAB]   = -0.20 / ATTR_RANGE(METAB) ;
  /* Fighting is easier for independent, high-mass, high-metab races:   */
  attr[FIGHT].cov[ADVENT] = -0.05 / ATTR_RANGE(ADVENT) ;
  attr[FIGHT].cov[MASS]   = -0.20 / ATTR_RANGE(MASS) ;
  attr[FIGHT].cov[METAB]  = -0.05 / ATTR_RANGE(METAB) ;
  /* A high metabolism is hard to support if you have high mass: */
  attr[METAB].cov[MASS]   =  0.15 / ATTR_RANGE(MASS) ;
  /* However, a large IQ is easier with high mass; (lot of brain space): */
  attr[A_IQ].cov[MASS]    = -0.10 / ATTR_RANGE(MASS) ;
  /* IQ is cheaper by virtue of collective intelligence: */
  attr[A_IQ].cov[COL_IQ]    =  -0.4 / ATTR_RANGE(COL_IQ) ;

  strcpy(attr[A_IQ].print_name, "IQ") ;
  }


void fix_up_iq()
{
  if (race.attr[COL_IQ] == 1.0)
    strcpy(attr[A_IQ].print_name, "IQ Limit") ;
  else
    strcpy(attr[A_IQ].print_name, "IQ") ;
  }



/**************
 * VERY IMPORTANT FUNCTION: this function is a representation function
 * for the race datatype.  That is, it will return a positive integer iff 
 * the race is NOT valid.  It is thus useful both when modifying a race and
 * when loading a race.  The file f should be NULL if you do not want to
 * print out descriptions of the errors; otherwise, error message(s) will be 
 * printed to that file.  
 */
int critique_to_file(f, rigorous_checking, is_player_race)
     FILE *f ;
     int rigorous_checking ;
     int is_player_race ;
{
#ifdef TRADITIONAL_RACES
  static char *tr_caveat = " in traditional GB" ;
#endif
  int i, nerrors = 0 ;

#define FPRINTF if (f != NULL) fprintf
  
  /*
   * Check for valid attributes: */
  for (i = FIRST_ATTRIBUTE; i<=LAST_ATTRIBUTE; i++) {
    if ((attr[i].is_integral == 2) && 
	(race.attr[i] != 0.0) && (race.attr[i] != 1.0)) {
      FPRINTF(f, "%s is boolean valued.  Use \"yes\" or \"no\".\n", 
	     attr[i].print_name) ;
      nerrors += 1 ;
      }
    if (race.attr[i] < attr[i].minimum) {
      FPRINTF(f, "%s must be at least %0.2f.\n", 
	     attr[i].print_name, attr[i].minimum) ;
      nerrors += 1 ;
      }
    if (race.attr[i] > attr[i].maximum) {
      FPRINTF(f, "%s may be at most %0.2f.\n", 
	     attr[i].print_name, attr[i].maximum) ;
      nerrors += 1 ;
      }
    /* Warning, but no error: */
    if (attr[i].is_integral) {
      if (race.attr[i] != ((double)((int)race.attr[i])))
	FPRINTF(f, "%s is integral; truncated to %1.0f.\n", attr[i].print_name, 
	       race.attr[i] = ((double)((int)race.attr[i]))) ;
	}
    else 
      if (race.attr[i] != (((double)((int)(100.0*race.attr[i])))/100.0))
	FPRINTF(f, "%s truncated to next lowest hundredth (%1.2f).\n",
	       attr[i].print_name, 
	       race.attr[i] = (((double)((int)(100.0*race.attr[i])))/100.0)) ;
    }


  /* 
   * Check for valid normal race attributes: */
  if (race.race_type == R_NORMAL) {
    if (race.attr[ABSORB] != 0.0) {
      FPRINTF(f, "Normal races do not absorb their enemies in combat.\n") ;
      nerrors += 1 ;
      }
    if (race.attr[PODS] != 0.0) {
      FPRINTF(f, "Normal races do not make pods.\n") ;
      nerrors += 1 ;
      }
#ifdef TRADITIONAL_RACES
    if (race.attr[COL_IQ] != 0.0) {
      FPRINTF(f, "Normal races may not have collective iq%s.\n", tr_caveat) ;
      nerrors += 1 ;
      }
    if (race.attr[FERT] != 0.0) {
      FPRINTF(f, "Normal races do not fertilize%s.\n", tr_caveat) ;
      nerrors += 1 ;
      }
    if (race.attr[SEXES] < 2.0) {
      FPRINTF(f, "Normal races must have at least two sexes%s.\n", tr_caveat) ;
      nerrors += 1 ;
      }
#endif
    }
  

  /* 
   * Check for valid morph race attributes: */
  if (race.race_type == R_METAMORPH) {
    if (race.attr[SEXES] > 1.0) {
      FPRINTF(f, "Metamorphs always have only one sex.\n") ;
      nerrors += 1 ;
      }

#ifdef TRADITIONAL_RACES
    if (race.attr[A_IQ] != DEFAULT_MESO_IQ_LIMIT) {
      FPRINTF(f, "Metamorphs' IQ limit may not be changed%s.\n", tr_caveat) ;
      nerrors += 1 ;
      } 
#endif
    }

  /*
   * Check for valid name:  */
  if (0 == strlen(race.name)) {
    FPRINTF(f, "Use a non-empty name.\n") ;
    nerrors += 1 ;
    }

  /*
   * Check for valid privileges:  */
  if ((race.priv_type < FIRST_PRIV_TYPE) ||
      (race.priv_type > LAST_PRIV_TYPE)) {
    FPRINTF(f, "Privileges out of valid range.\n") ;
    nerrors += 1 ;
    }
  if ((race.priv_type != P_NORMAL) && is_player_race) {
    FPRINTF(f, "Players may not create %s races.\n", 
	   priv_print_name[race.priv_type]) ;
    nerrors += 1 ;
    }

  /*
   * Check for valid home planet: */
  if ((race.home_planet_type < FIRST_HOME_PLANET_TYPE) ||
      (race.home_planet_type > LAST_HOME_PLANET_TYPE)) {
    FPRINTF(f, "Home planet type out of valid range.\n") ;
    nerrors += 1 ;
    }

  /*
   * Check for valid race: */
  if ((race.race_type < FIRST_RACE_TYPE) ||
      (race.race_type > LAST_RACE_TYPE)) {
    FPRINTF(f, "Home planet type out of valid range.\n") ;
    nerrors += 1 ;
    }

  /*
   * Check for valid sector compats: */
  if ((race.home_planet_type != H_JOVIAN)&&(race.compat[S_PLATED] != 100.0)) {
    FPRINTF(f, "Non-jovian races must have 100%% plated compat.\n") ;
    nerrors += 1 ;
    }
  for (i = FIRST_SECTOR_TYPE+1; i<=LAST_SECTOR_TYPE; i++) {
    if (race.compat[i] < 0.0) {
      FPRINTF(f, "Sector compatibility is at minimum 0%%.\n") ;
      nerrors += 1 ;
      }
    if (race.compat[i] > 100.0) {
      FPRINTF(f, "Sector compatibility may be at most 100%%.\n") ;
      nerrors += 1 ;
      }
    if ((i == S_GAS) && (race.compat[i] != 0.0) &&
	(race.home_planet_type != H_JOVIAN)) {
      FPRINTF(f, "Non-jovian races may never have gas compatibility!\n") ;
      nerrors += 1 ;
      }
    if ((i != S_GAS) && (race.compat[i] != 0.0) &&
	(race.home_planet_type == H_JOVIAN)) {
      FPRINTF(f, "Jovian races may have no compatibility other than gas!\n") ;
      nerrors += 1 ;
      }
    /* A warning, but no error: */
    if (race.compat[i] != ((double)((int)race.compat[i])))
      FPRINTF(f, "Sector compatibilities are integral; truncated to %1.0f.\n",
	     race.compat[i] = ((double)((int)race.compat[i]))) ;
    }



  if (rigorous_checking) {
    /*
     * Any rejection notice is an error: */
    if (strlen(race.rejection)) {
      FPRINTF(f, "%s", race.rejection) ;
      nerrors += 1 ;
      }
    /*
     * Check for valid password: */
    if (MIN_PASSWORD_LENGTH > strlen(race.password)) {
      FPRINTF(f, "Passwords are required to be at least %d characters long.\n",
	      MIN_PASSWORD_LENGTH) ;
      nerrors += 1 ;
      }
    /*
     * Check that race isn't 'superrace': */
    if (npoints < 0) {
      FPRINTF(f, "You can't have negative points left!\n");
      nerrors += 1 ;
      }
    /*
     * Check that sector compats are reasonable. */
    if ((race.home_planet_type != H_JOVIAN) && (race.n_sector_types == 1)) {
      FPRINTF(f, "Non-jovian races must be compat with at least one sector type besides plated.\n") ;
      nerrors += 1 ;
      }
    for (i=FIRST_SECTOR_TYPE; i<=LAST_SECTOR_TYPE; i++)
      if ((planet_compat_cov[race.home_planet_type][i] == 1.0) &&
	  (race.compat[i] == 100.0))
	break ;
    if (i == N_SECTOR_TYPES) {
      FPRINTF(f, "You must have 100%% compat with at least one sector type that is common on\n  your home planet type.  (Marked with a '*')\n") ;
      nerrors += 1 ;
      }
    }
  if (race.status >= 0)
    race.status = (nerrors == 0) ? STATUS_BALANCED : STATUS_UNBALANCED ;
  return nerrors ;
#undef FPRINTF
  }


int critique_modification()
{
  int nerrors ;

  race.rejection[0] = '\0' ;
  nerrors = critique_to_file(stdout, 0, IS_PLAYER) ;
  if (nerrors)
    bcopy(&last, &race, sizeof(struct x)) ;
  else
    changed = altered = 1 ;
  race.status = (nerrors == 0) ? STATUS_BALANCED : STATUS_UNBALANCED ;
  return nerrors ;
  }




/**************
 * Initialize the race to the init value, and set the l_fudge values
 * accordingly so that the cost of this race's attributes is zero.
 */
void initialize()
{
  int i ;

  bzero(&race, sizeof(race)) ;
  for (i=FIRST_ATTRIBUTE; i<=LAST_ATTRIBUTE; i++)
    race.attr[i] = attr[i].init ;
  race.race_type = R_NORMAL ;
  race.priv_type = P_NORMAL ;
  race.home_planet_type = H_EARTH ;
  race.n_sector_types = 1 ;
  race.compat[S_PLATED] = 100 ;
  strcpy(race.name, "Unknown") ;
  strcpy(race.password, "XXX") ;
  strcpy(race.address, TO) ;
  normal() ;
  bcopy(&race, &last, sizeof(struct x)) ;
  cost_of_race() ;
  for (i=FIRST_ATTRIBUTE; i<=LAST_ATTRIBUTE; i++)
    attr[i].l_fudge += -cost.attr[i] ;
  cost_of_race() ;
#ifdef ENROLL
  init_enroll() ;
#endif
  }



/**************
 * Now the functions that implement racegen's commands.  Rather than me
 * trying to tell you about them here, just run the program and diddle
 * with it to get the idea.
 */
void help(argc, argv)
     int argc ;
     char *argv[] ;
{
#ifdef ENROLL
  int enroll, process ;
#endif
  int i, j, helpp, load, modify, print, save, send, quit ;

  if (argc == 1) {
#ifdef ENROLL
    enroll = process =
#endif
    helpp = load = modify = print = save = send = quit = 1 ;
 printf("\n") ;
 printf("To execute a command, type it at the command line.  All commands\n") ;
 printf("and command arguments maybe either upper or lower case, and/or\n") ;
 printf("abbreviated.  The available commands are:\n") ;
    }
  else {
#ifdef ENROLL
    enroll = process =
#endif
    helpp = load = modify = print = save = send = quit = 0 ;
    for (i=1; i<argc; i++) {
      j = strlen(argv[i]) ;
#ifdef ENROLL
      if      (! strncasecmp(argv[i], "enroll", j))
	enroll = 1 ;   else
#endif
      if      (! strncasecmp(argv[i], "help", j))
	helpp = 1 ;
      else if (! strncasecmp(argv[i], "load", j))
	load = 1 ;
      else if (! strncasecmp(argv[i], "modify", j))
	modify = 1 ;
      else if (! strncasecmp(argv[i], "print", j))
	print = 1 ;
#ifdef ENROLL
      else if (! strncasecmp(argv[i], "process", j))
	process = 1 ;
#endif
      else if (! strncasecmp(argv[i], "save", j))
	save = 1 ;
      else if (! strncasecmp(argv[i], "send", j))
	send = 1 ;
      else if (! strncasecmp(argv[i], "quit", j))
	quit = 1 ;
      else {
	printf("\n") ;
	printf("\"%s\" is not a command.\n", argv[i]) ;
	}
      }
    }
    
#ifdef ENROLL
  if (enroll) {
    printf("\n") ;
    printf("Enroll\n") ;
    printf("\t\t This command will add the current race to the game,\n") ;
    printf("\t\t after checking to make sure it has all points spent, and\n") ;
    printf("\t\t other such administrivia.\n") ;
    }
#endif

  if (helpp) {
    printf("\n") ;
    printf("Help [command]*\n") ;
    printf("\t\t This command gives you information about racegen's\n") ;
    printf("\t\t commands.  If called with no arguments, it will print\n") ;
    printf("\t\t information about all available commands.  Otherwise it\n") ;
    printf("\t\t only prints information about the specified commands.\n") ;
    }

  if (load) {
    printf("\n") ;
    printf("Load [filename]\n") ;
    printf("\t\t This command will load your race from the file specified\n") ;
    printf("\t\t in the optional first argument, or from the file \"%s\"\n", 
	   (race.filename[0] ? race.filename : SAVETO)) ;
    printf("\t\t if no argument is given.\n") ;
    }

  if (modify) {
    printf("\n") ;
    printf("Modify arg1 arg2\n") ;
    printf("\t\t This command allows you to change the values of your\n") ;
    printf("\t\t race's name, password, type, attributes, planet, and compats.\n") ;
    printf("\t\t The syntax is as follows:\n") ;
    printf("\t\t   <modify>     ::= modify <attr> <value>\n") ;
#ifdef ENROLL
    printf("\t\t                  | modify from <string>\n") ;
#endif
    printf("\t\t                  | modify name <string>\n") ;
    printf("\t\t                  | modify password <string>\n") ;
    printf("\t\t                  | modify planet <planettype>\n") ;
#ifdef ENROLL
    printf("\t\t                  | modify privilege <privtype>\n") ;
#endif
    printf("\t\t                  | modify race <racetype>\n") ;
    printf("\t\t                  | modify <sectortype> <value>\n") ;

    printf("\t\t   <attribute>  ::= %s", attr[0].print_name) ;
    for (i = FIRST_ATTRIBUTE+1; i<LAST_ATTRIBUTE; i++) {
      printf(" | %s", attr[i].print_name) ;
      if ((i % 3) == 2)
	printf("\n\t\t                 ") ;
      }
    printf("\n") ;

    printf("\t\t   <planettype> ::= %s", planet_print_name[0]) ;
    for (i = FIRST_HOME_PLANET_TYPE+1; i<=min(4,LAST_HOME_PLANET_TYPE); i++)
      printf(" | %s", planet_print_name[i]) ;
    printf("\n\t\t                 ") ;
    for (; i<=LAST_HOME_PLANET_TYPE; i++)
      printf(" | %s", planet_print_name[i]) ;
    printf("\n") ;

    printf("\t\t   <racetype>   ::= %s", race_print_name[0]) ;
    for (i = FIRST_RACE_TYPE+1; i<=LAST_RACE_TYPE; i++)
      printf(" | %s", race_print_name[i]) ;
    printf("\n") ;

    printf("\t\t   <sectortype> ::= %s", sector_print_name[1]) ;
    for (i = FIRST_SECTOR_TYPE+2; i<=min(5,LAST_SECTOR_TYPE); i++)
      printf(" | %s", sector_print_name[i]) ;
    printf("\n\t\t                 ") ;
    for (; i<=LAST_SECTOR_TYPE; i++)
      printf(" | %s", sector_print_name[i]) ;
    printf("\n") ;
    }

  if (print) {
    printf("\n") ;
    printf("Print [filename]\n") ;
    printf("\t\t With no argument, this command prints your race to the\n") ;
    printf("\t\t screen.  It is automatically executed after each modify.\n") ;
    printf("\t\t Otherwise it saves a text copy of your race to the file\n") ;
    printf("\t\t specified in the first argument.\n") ;
    }

#ifdef ENROLL
  if (process) {
    printf("\n") ;
    printf("Process filename\n") ;
    printf("\t\t This command will repeatedly load races from filename,\n") ;
    printf("\t\t and then try to enroll them.  You can thus easily \n") ;
    printf("\t\t enroll tens of players at once.  \n") ;
    }
#endif

  if (save) {
    printf("\n") ;
    printf("Save [filename]\n") ;
    printf("\t\t This command will save your race to the file specified in\n");
    printf("\t\t the optional first argument, or to the file \"%s\"\n", 
	   (race.filename[0] ? race.filename : SAVETO)) ;
    printf("\t\t if no argument is given.\n") ;
    }

  if (send) {
    printf("\n") ;
    printf("Send\n") ;
    printf("\t\t This command will send your race to God, (%s).\n", TO) ;
    printf("\t\t It will not work unless you have spent all your points.\n") ;
    }

  if (quit) {
    printf("\n") ;
    printf("Quit\n") ;
    printf("\t\t This command will prompt you to save your work if you\n") ;
    printf("\t\t haven't already, and then exit this program.\n") ;
    }

  printf("\n") ;
  }


/*
 * Return non-zero on failure, zero on success. */
int load_from_file(g)
     FILE *g ;
{
  int i ;
  char buf[80], from_address[80] ;

#define FSCANF(file, format, variable) \
  if (EOF == fscanf((file), (format), (variable))) \
    goto premature_end_of_file
  
  do {
    FSCANF(g, " %s", buf) ;
    if (0 == strcmp(buf, "From:")) {
      FSCANF(g, " %s", buf) ;
      strcpy(from_address, buf) ;
      }
    }
  while (strcmp(buf, START_RECORD_STRING)) ;

  race.status = STATUS_BALANCED ;
  FSCANF(g, " %s", race.address) ;
  if (from_address[0])
    strcpy(race.address, from_address) ;
  FSCANF(g, " %s", race.name) ;
  FSCANF(g, " %s", race.password) ;
  FSCANF(g, " %d", &race.priv_type) ;
  FSCANF(g, " %d", &race.home_planet_type) ;
  FSCANF(g, " %d", &race.race_type) ;
  if (race.race_type == R_NORMAL)
    normal() ;
  else
    metamorph() ;
  for (i=FIRST_ATTRIBUTE; i<=LAST_ATTRIBUTE; i++)
    FSCANF(g, " %lf", &race.attr[i]) ;
  fix_up_iq() ;
  for (i=FIRST_SECTOR_TYPE;  i<=LAST_SECTOR_TYPE; i++)
    FSCANF(g, " %lf", &race.compat[i]) ;
  do {
    FSCANF(g, " %s", buf) ;
    }
  while (strcmp(buf, END_RECORD_STRING)) ;
  return 0 ;
 premature_end_of_file:
  printf("Error: premature end of file.\n") ;
  return 1 ;
  }

/*
 * Return non-zero on failure, zero on success. */
static int load_from_filename(filename)
     char *filename ;
{
  int ret ;
  FILE *f = fopen(filename, "r") ;

  if (f == NULL) {
    printf("Unable to open file \"%s\".\n", filename) ;
    return 1 ;
    }
  ret = load_from_file(f) ;
  fclose(f) ;
  return ret ;
  }

void load(argc, argv)
     int argc ;
     char *argv[] ;
{
  char c[64] ;
  int i ;

  bcopy(&race, &last, sizeof(struct x)) ;
  if (altered) {
    i = Dialogue("This race has been altered; load anyway?", "yes", "no", 0) ;
    if (i == 1)
      return ;
    }
  if (argc > 1)
    strcpy(c, argv[1]) ;
  else if (! race.filename[0])
    strcpy(c, SAVETO) ;
  if (load_from_filename(c)) {
    printf("Load from file \"%s\" failed.\n", c) ;
    bcopy(&last, &race, sizeof(struct x)) ;
    }
  else {
    printf("Loaded race from file \"%s\".\n", c) ;
    strcpy(race.filename, c) ;
    altered = 0 ;
    changed = 1 ;
    }
  }

int modify(argc, argv)
     int argc ;
     char *argv[] ;
{
  int i, j ;
  static char *help_strings[2] = {NULL, "modify"} ;
  double f ;

  if (argc < 3) {
    help(2, help_strings) ;
    return -1 ;
    }
  j = strlen(argv[1]) ;

  bcopy(&race, &last, sizeof(struct x)) ;

  /*
   * Check for attribute modification: */
  for (i = FIRST_ATTRIBUTE; i<=LAST_ATTRIBUTE; i++)
    if (! strncasecmp(argv[1], attr[i].print_name, j)) {
      if (attr[i].is_integral == 2) {    /* Boolean attribute. */
	j = strlen(argv[2]) ;
	if (! strncasecmp(argv[2], "no", j))
	  f = 0.0 ;
	else if (! strncasecmp(argv[2], "yes", j))
	  f = 1.0 ;
	else
	  f = atof(argv[2]) ;
	}
      else
	f = atof(argv[2]) ;
      
      race.attr[i] = f ;
      fix_up_iq() ;
      return critique_modification() ;
      }

  /*
   * Check for name modification:  */
  if (! strncasecmp(argv[1], "name", j)) {
    strcpy(race.name, argv[2]) ;
    return critique_modification() ;
    }

#ifdef ENROLL
  /*
   * Check for from-address modification:  */
  if (! strncasecmp(argv[1], "from:", j)) {
    strcpy(race.address, argv[2]) ;
    return critique_modification() ;
    }
  /*
   * Check for privilege modification:  */
  if (! strncasecmp(argv[1], "privilege", j)) {
    j = strlen(argv[2]) ;
    for (i = FIRST_PRIV_TYPE; i<=LAST_PRIV_TYPE; i++)
      if (! strncasecmp(argv[2], priv_print_name[i], j)) {
	race.priv_type = i ;
	return critique_modification() ;
	}
    race.priv_type = atof(argv[2]) ;
    return critique_modification() ;
    }
#endif

  /*
   * Check for planet modification:  */
  if (! strncasecmp(argv[1], "planet", j)) {
    j = strlen(argv[2]) ;
    for (i = FIRST_HOME_PLANET_TYPE; i<=LAST_HOME_PLANET_TYPE; i++)
      if (! strncasecmp(argv[2], planet_print_name[i], j)) {
	if (i == H_JOVIAN) {
	  bzero(race.compat, sizeof(race.compat)) ;
	  race.compat[S_GAS] = 100.0 ;
	  }
	else if (race.home_planet_type == H_JOVIAN) {
	  race.compat[S_PLATED] = 100.0 ;
	  race.compat[S_GAS] = 0.0 ;
	  }
	race.home_planet_type = i ;
	return critique_modification() ;
	}
    printf("\"%s\" is not a valid planet type.\n", argv[2]) ;
    return -1 ;
    }

  /*
   * Check for password modification:  */
  if (! strncasecmp(argv[1], "password", j)) {
    strcpy(race.password, argv[2]) ;
    return critique_modification() ;
    }

  /*
   * Check for race modification:  */
  if (! strncasecmp(argv[1], "race", j)) {
    j = strlen(argv[2]) ;
    for (i = FIRST_RACE_TYPE; i<=LAST_RACE_TYPE; i++)
      if (! strncasecmp(argv[2], race_print_name[i], j)) {
	if (i == R_METAMORPH) {
	  race.attr[ABSORB] = 1 ;
	  race.attr[PODS] = 1 ;
	  race.attr[COL_IQ] = 1 ;
	  race.attr[SEXES] = 1 ;
	  race.attr[A_IQ] = DEFAULT_MESO_IQ_LIMIT ;
	  metamorph() ;
	  }
	else {
	  race.attr[ABSORB] = 0 ;
	  race.attr[PODS] = 0 ;
	  race.attr[COL_IQ] = 0 ;
	  race.attr[SEXES] = 2 ;
	  race.attr[A_IQ] = 125 ;
	  normal() ;
	  }
	race.race_type = i ;
	return critique_modification() ;
	}
    printf("\"%s\" is not a valid race type.\n", argv[2]) ;
    return -1 ;
    }


  /*
   * Check for sector_type modification: */
  for (i = FIRST_SECTOR_TYPE; i<=LAST_SECTOR_TYPE; i++)
#ifndef ENROLL
    if (i == S_PLATED)
      continue ;    /* Players should never need to modify this. */
    else
#endif
    if (! strncasecmp(argv[1], sector_print_name[i], j)) {
      race.compat[i] = atof(argv[2]) ;
      return critique_modification() ;
      }
  
  /*
   * Print error */
  printf("\n") ;
  printf("Modify: didn't recognize the first argument \"%s\".\n", argv[1]) ;
  printf("Type \"help modify\" for more information on modify.\n") ;
  printf("\n") ;
  return -1 ;
  }

void print_to_file(f, verbose)
     FILE *f ;
     int verbose ;
{
#define FPRINTF if (verbose) fprintf
  int i ;

#ifdef ENROLL
  FPRINTF(f, "From:") ;
#endif
  if (! verbose) {
    fprintf(f, START_RECORD_STRING) ;
    fprintf(f, " %s", race.address) ;
    }
#ifdef ENROLL
  else
    fprintf(f, " %s", race.address) ;
#endif
  FPRINTF(f, "\n") ;

  FPRINTF(f, "Name:") ;
  fprintf(f, " %16.16s", race.name) ;
  FPRINTF(f, "\n") ;

  FPRINTF(f, "Password:") ;
  fprintf(f, " %12.12s", race.password) ;
  FPRINTF(f, "\n") ;

#ifdef ENROLL
  FPRINTF(f, "Privileges:") ;
  if (verbose)
    fprintf(f, "%11.11s", priv_print_name[race.priv_type]) ;
  else
#endif
  if (! verbose)
    fprintf(f, " %d", race.priv_type) ;
#ifdef ENROLL
  FPRINTF(f, "\n") ;
#endif

  FPRINTF(f, "Home Planet:") ;
  if (verbose)
    fprintf(f, " %9.9s", planet_print_name[race.home_planet_type]) ;
  else
    fprintf(f, " %d", race.home_planet_type) ;
  FPRINTF(f, "  [%4d]\n", cost.home_planet_type) ;

  FPRINTF(f, "Race type:") ;
  if (verbose)
    fprintf(f, "%12.12s", race_print_name[race.race_type]) ;
  else
    fprintf(f, " %d", race.race_type) ;
  FPRINTF(f, "  [%4d]\n", cost.race_type) ;
  FPRINTF(f, "\n") ;

  FPRINTF(f, "Attributes:\n") ;
  for (i=FIRST_ATTRIBUTE; i<=LAST_ATTRIBUTE; i++) {
    FPRINTF(f, "%13.13s:", attr[i].print_name) ;
    if (verbose && (attr[i].is_integral == 2))
      fprintf(f, (race.attr[i] > 0.0) ? "  yes   " : "   no   ") ;
    else
      fprintf(f, " %7.2f", race.attr[i]) ;
    FPRINTF(f, "  [%4.0f]", cost.attr[i]) ;
    FPRINTF(f, (i & 01) ? "\n" : "     ") ;
    }
  if (i & 01)
    FPRINTF(f, "\n") ;
  FPRINTF(f, "\n") ;

  FPRINTF(f, "Sector Types:    %2d     [%4d]\n", 
	  race.n_sector_types, cost.n_sector_types) ;
  for (i=FIRST_SECTOR_TYPE;  i<=LAST_SECTOR_TYPE; i++) {
    FPRINTF(f, "%13.13s: ", sector_print_name[i]) ;
    fprintf(f, " %3.0f", race.compat[i]) ;
    FPRINTF(f, "%%   %c[%4.0f]", 
	    (planet_compat_cov[race.home_planet_type][i] == 1.0) ? '*' : ' ',
	    cost.compat[i]) ;
    FPRINTF(f, (i & 01) ? "\n" : "     ") ;
    }

  if (! verbose)
    fprintf(f, END_RECORD_STRING) ;
  FPRINTF(f, "\n") ;
  FPRINTF(f, "Points left: %d          Previous value: %d\n", 
	  npoints, last_npoints) ;
  fprintf(f, "\n") ;
#undef FPRINTF
  }

static int print_to_filename(filename, verbose)
     char *filename ;
     int verbose ;
{
  FILE *f = fopen(filename, "w") ;

  if (f == NULL) {
    printf("Unable to open file \"%s\".\n", filename) ;
    return 0 ;
    }
  print_to_file(f, verbose) ;
  fclose(f) ;
  return 1 ;
  }

void print(argc, argv)
     int argc ;
     char *argv[] ;
{
  if (argc == 1)
    print_to_file(stdout, 1) ;
  else if (print_to_filename(argv[1], 1))
    printf("Printed race to file \"%s\".\n", argv[1]) ;
  }

void save(argc, argv)
     int argc ;
     char *argv[] ;
{
  if (argc > 1)
    strcpy(race.filename, argv[1]) ;
  else if (! race.filename[0])
    strcpy(race.filename, SAVETO) ;
  if (print_to_filename(race.filename, 0)) {
    printf("Saved race to file \"%s\".\n", race.filename) ;
    altered = 0 ;
    }
  }

void send(argc, argv)
     int argc ;
     char *argv[] ;
{
  FILE *f ;
  char sys[64] ;

  bcopy(&race, &last, sizeof(struct x)) ;
  if (critique_to_file(stdout, 1, IS_PLAYER))
    return ;

  f = fopen(TMP, "w") ;
  if (f == NULL) {
    printf("Unable to open file \"%s\".\n", TMP) ;
    return ;
    }
  fprintf(f, "To: %s\n", race.address) ;
  fprintf(f, "Subject: %s Race Submission\n", GAME) ;
  fprintf(f, "\n") ;
  print_to_file(f, 0) ;
  fclose(f) ;

  printf("Sending to %s via %s...", TO, MAILER) ;
  fflush(stdout) ;
  sprintf(sys, "cat %s | %s %s", TMP, MAILER, TO) ;
  system(sys) ;
  printf("done.\n") ;
  }

int Dialogue(prompt, va_alist)
     char *prompt;
		 va_dcl 
{
	va_list ap;
  char input[128] ;
  char *carg;
	int len, i, argc = 0 ;
  int init = 0;
	char *argv[16] ;
  printf("%s", prompt) ;
  va_start(ap);
	while ((carg = va_arg(ap, char *))!=0) {
    if (!init) {
			printf(" [%s", carg) ;
			init = 1;
		} else {
			printf("/%s", carg);
		}
    argv[argc++] = carg++ ;
  }
	va_end(ap);
  if (argc)
    printf("]") ;
  printf("> ") ;
  while (1) {
    fgets(input, 128, stdin) ;
    if (argc == 0)
      return -1 ;
    len = strlen(input) - 1 ;
    
    for (i = 0; i < argc; i++)
      if (! strncasecmp(argv[i], input, len))
	return i ;
    /*
     * The input did not match any of the valid responses: */
    printf("Please enter ") ;
    for (i = 0; i < argc-1; i++)
      printf("\"%s\", ", argv[i]) ;
    printf("or \"%s\"> ", argv[i]) ;
    }
  }


void quit(argc, argv)
     int argc ;
     char *argv[] ;
{
  int i ;

  if (please_quit)  /* This could happen if ^c is hit while here. */
    exit(0) ;
  please_quit = 1 ;
  if (altered) {
    i = Dialogue("Save this race before quitting?", "yes", "no", "abort", 0) ;
    if (i == 0)   
      save(1, NULL) ;
    else if (i == 2)
      please_quit = 0 ;
    }
  }



static void control_c_handler()
{
  printf("\n") ;
  exit(0) ;
  }


/**************
 * This function merely takes the space-parsed command line and executes
 * one of the commands above.
 */
void execute(argc, argv)
     int argc ;
     char *argv[] ;
{
  int i ;

#if 0
  for (i=0; i < argc; i++)
    printf("%d: \"%s\"\n", i, argv[i]) ;
#endif
  if (argc == 0) {
    printf("Type \"help\" for help.\n") ;
    return ;
    }
  i = strlen(argv[0]) ;
#ifdef ENROLL
  if      (! strncasecmp(argv[0], "enroll", i))
    enroll(argc, argv) ;
  else
#endif
  if      (! strncasecmp(argv[0], "help", i))
    help(argc, argv) ;
  else if (! strncasecmp(argv[0], "load", i))
    load(argc, argv) ;
  else if (! strncasecmp(argv[0], "modify", i))
    modify(argc, argv) ;
  else if (! strncasecmp(argv[0], "print", i))
    print(argc, argv) ;
#ifdef ENROLL
  else if (! strncasecmp(argv[0], "process", i))
    process(argc, argv) ;
#endif
  else if (! strncasecmp(argv[0], "save", i))
    save(argc, argv) ;
  else if (! strncasecmp(argv[0], "send", i))
    send(argc, argv) ;
  else if (! strncasecmp(argv[0], "quit", i))
    quit(argc, argv) ;
  else {
    printf("Unknown command \"%s\".  Type \"help\" for help.\n", argv[0]) ;
    return ;
    }
  }




/**************
 * Here is the main loop, where I print the command prompt, parse it into
 * words using spaces as the separator, and call execute.  Level is the
 * number of higher level modify print loops above this one.  It will
 * always be zero for player racegens.
 */
void modify_print_loop(level)
     int level ;
{
  char buf[128], *com, *args[4] ;
  int i ;

  while (! please_quit) {
    last_npoints = npoints ;
    npoints = STARTING_POINTS - cost_of_race() ;

    if (changed) {
      print_to_file(stdout, 1) ;
      changed = 0 ;
      }
#ifdef ENROLL
    printf("%s [enroll/help/load/modify/print/process/save/send/quit]> ",
	   level ? "Fix" : "Command") ;
#else
    printf("Command [help/load/modify/print/save/send/quit]> ") ;
#endif
    fflush(stdout) ;
    com = fgets(buf, 128, stdin) ;
    buf[strlen(buf)-1] = '\0' ;

    for (i=0; i<4; i++) {
      while (*com && (*com == ' '))   
	*com++ = '\0' ;
      if (! *com)
	break ;
      args[i] = com ;
      while (*com && (*com != ' '))   
	com++ ;
      }
    execute(i, args) ;
    }
  printf("\n") ;
  }


/**************
 * Print out initial info and then call modify-print loop.
 */
int main(argc, argv)
     int argc ;
     char *argv[] ;
{
  signal(SIGINT, control_c_handler) ;
  signal(SIGQUIT, control_c_handler) ;
  initialize() ;
  printf("\n") ;
  printf("\n") ;
  printf("Galactic Bloodshed Race Generator %s\n", VERSION) ;
  printf("\n") ;
  printf("Finished races will be sent to %s.\n", TO) ;
  printf("***************************************************************\n") ;
  printf("Game: %s, using %s\n", GAME, GB_VERSION) ;
  printf("Address: %s\n", LOCATION) ;
  printf("God: %s\n", MODERATOR) ;
  printf("Starts: %s\n", STARTS) ;
  printf("Stars: %s; Players: %s\n", STARS, PLAYERS) ;
  printf("Any race may be refused or modified by God for any reason!\n") ;
  printf("\n") ;
  printf("DEADLINE: %s\n", DEADLINE) ;
  printf("***************************************************************\n") ;
  printf("Update schedule:\n") ;
  printf("%s\n", UPDATE_SCH) ;
  printf("\n") ;
  printf("If you cannot make this update schedule, do NOT send in a race!\n") ;
  printf("***************************************************************\n") ;
  printf(OTHER_STUFF) ;
  printf("\n") ;
  Dialogue("Hit return", 0) ;
 
  modify_print_loop(0) ;

  return 0 ;
  }
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads