|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T m
Length: 28721 (0x7031) Types: TextFile Names: »map.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/X/Xconq/map.c«
/* Copyright (c) 1987, 1988 Stanley T. Shebs, University of Utah. */ /* This program may be used, copied, modified, and redistributed freely */ /* for noncommercial purposes, so long as this notice remains intact. */ /* RCS $Header: map.c,v 1.1 88/06/21 12:30:19 shebs Exp $ */ /* Code relating to the reading and writing of mapfiles. A mapfile comes */ /* in several independent sections, each of whose presence is flagged in the */ /* header line. A mapfile may also specify the inclusion of other mapfiles. */ #include "config.h" #include "misc.h" #include "dir.h" #include "period.h" #include "side.h" #include "unit.h" #include "map.h" #include "global.h" /* A mapfile header has various pieces that we need to save. */ typedef struct a_header { int numincls; /* number of included files */ char *includes[MAXINCLUDES]; /* raw names of included files */ int numnotes; /* number of lines in designer notes */ char *notes[MAXMAPNOTES]; /* designer notes for mapfile */ char *sections; /* +/- string of mapfile sections */ int sdetail; int udetail; } Header; /* Sections of a mapfile have their recognizers. */ #define vsect(s) (s[0] == '+') #define psect(s) (s[1] == '+') #define msect(s) (s[2] == '+') #define gsect(s) (s[3] == '+') #define ssect(s) (s[4] == '+') #define usect(s) (s[5] == '+') /* These vars tell init code what it has to do itself. */ extern bool periodmade, mapmade, globalsmade, sidesmade, unitsmade; extern int nextid; World world; /* the world structure itself! */ Header *mainhead = NULL; /* the "top-level" map header - needs saving */ int nummaps; /* Number of mapfiles loaded */ int curgiven; /* tmp for side creation */ int nux, nuy; /* where to put a newly created unit */ int nusn; /* Number of unit's side */ int nut; /* Number of the Unit Transport */ char *mapfilenames[MAXLOADED]; /* Names of loaded files */ char *perfilename = NULL; /* Malloc just enough space for the map. */ allocate_map() { world.terrain = (Hex *) malloc(world.width * world.height * sizeof(Hex)); } /* Game files can live in library directories or somewhere else. This */ /* function tries to find a file, open it, and load the contents. */ load_mapfile(name) char *name; { bool loaded = FALSE; int i; FILE *fp = NULL; make_pathname(XCONQLIB, name, "", spbuf); for (i = 0; i < nummaps; ++i) { if (strcmp(name, mapfilenames[i]) == 0) { loaded = TRUE; break; } if (strcmp(spbuf, mapfilenames[i]) == 0) { loaded = TRUE; break; } } if (loaded) { fprintf(stderr, "\"%s\" is already loaded.\n", name); } else if ((fp = fopen(spbuf, "r")) != NULL) { if (Debug) printf("Reading \"%s\" ...\n", spbuf); mapfilenames[nummaps++] = copy_string(spbuf); } else if ((fp = fopen(name, "r")) != NULL) { if (Debug) printf("Reading \"%s\" ...\n", name); mapfilenames[nummaps++] = copy_string(name); } else { fprintf(stderr, "Neither \"%s\" or \"%s\" could be opened!\n", spbuf, name); exit(1); } if (fp != NULL) { load_sections(fp); fclose(fp); } if (periodmade) { perfilename = name; periodmade = FALSE; } } /* Grab up all the components of a mapfile, and recurse if any submapfiles. */ /* The header line tells us which sections to try to load. For some */ /* sections, flag them as loaded so we know not to synthesize replacements. */ load_sections(fp) FILE *fp; { char sect[BUFSIZE], *tmp; int numfiles, i; Header *head; /* Read and interpret the header */ head = (Header *) malloc(sizeof(Header)); if (mainhead == NULL) mainhead = head; fscanf(fp, "Xconq %d %s", &numfiles, sect); head->numincls = numfiles; head->sections = copy_string(sect); head->sdetail = head->udetail = 0; tmp = read_line(fp); head->notes[0] = tmp; head->numnotes = 1; if (tmp[strlen(tmp)-1] == ';') { for (i = 1; i < MAXMAPNOTES; ++i) { head->notes[i] = read_line(fp); if (strcmp(head->notes[i], ".") == 0) { head->numnotes = i; break; } if (Debug) printf("%s\n", head->notes[i]); } } for (i = 0; i < numfiles; ++i) { head->includes[i] = read_line(fp); if (Debug) printf("Including %s ...\n", head->includes[i]); load_mapfile(head->includes[i]); } /* Suck in all the sections */ if (vsect(sect)) { read_version(fp); } if (psect(sect)) { read_period(fp); periodmade = TRUE; } if (msect(sect)) { read_map(fp); mapmade = TRUE; } if (gsect(sect)) { read_globals(fp); globalsmade = TRUE; } if (ssect(sect)) { head->sdetail = read_sides(fp); sidesmade = TRUE; } if (usect(sect)) { head->udetail = read_units(fp); unitsmade = TRUE; } } /* Read the version line and test it against the program version. */ /* Continuation after failure is likely to result in core dump, so leave. */ read_version(fp) FILE *fp; { char *tmpver; if (Debug) printf("Will try to read version ...\n"); tmpver = read_line(fp); if (strcmp(tmpver, version) != 0) { fprintf(stderr, "Sorry, mapfile is for some other version!\n"); fprintf(stderr, "Version should be \"%s\", not \"%s\".\n", tmpver, version); exit(1); } if (Debug) printf("... Done reading version.\n"); } /* Read the map section, starting with header, then working through data. */ /* The map data may be partly run-length encoded; this is recognized by the */ /* presence of digit strings (of arbitrary length) followed by the run char. */ /* Any or all of the data may be so encoded, otherwise the chars are 1-1 */ /* with map hexes; newlines are always at the end of each map row. */ read_map(fp) FILE *fp; { char ch; int extension, x, y, terr, run; fscanf(fp, "Map %d %d %d %d %d\n", &world.width, &world.height, &world.scale, &world.known, &extension); if (Debug) printf("Will try to read %dx%d map ...\n", world.width, world.height); allocate_map(); for (y = world.height-1; y >= 0; --y) { for (x = 0; x < world.width; /* incr below */) { ch = getc(fp); if (isdigit(ch)) { run = ch - '0'; while (isdigit(ch = getc(fp))) { run = run * 10 + ch - '0'; } } else { run = 1; } terr = find_terr(ch); if (terr < 0) terr = period.defaultterrain; if (terr < 0) { fprintf(stderr, "'%c' is not valid terrain in this period!\n", ch); exit(1); } while (run-- > 0) { set_terrain_at(x, y, terr); set_people_at(x, y, NOBODY); set_unit_at(x, y, NULL); ++x; } } fscanf(fp, "\n"); } if (Debug) printf("... Done reading map.\n"); } /* This should be more efficient. Fortunately, run-length encoded maps */ /* don't call it for every single hex! */ find_terr(ch) char ch; { register int t; for_all_terrain_types(t) if (ch == ttypes[t].tchar) return t; return (-1); } /* Period reading is complicated, and therefore lives in its own file. */ /* Read the globals section. Most info of interest is in the header, but */ /* win/lose conditions need extra reads. */ read_globals(fp) FILE *fp; { int extension, i, u, r; Condition *cond; fscanf(fp, "Globals %d %d %d %d %d %d\n", &global.time, &global.endtime, &global.setproduct, &global.leavemap, &global.numconds, &extension); if (Debug) printf("Will try to read globals ...\n"); for (i = 0; i < global.numconds; ++i) { cond = &(global.winlose[i]); fscanf(fp, "%d %d %d %d %d\n", &(cond->win), &(cond->type), &(cond->starttime), &(cond->endtime), &(cond->sidesn)); switch (cond->type) { case TERRITORY: fscanf(fp, "%d\n", &(cond->n)); break; case UNITCOUNT: for_all_unit_types(u) { fscanf(fp, "%d", &(cond->units[u])); } fscanf(fp, "\n"); break; case RESOURCECOUNT: for_all_resource_types(r) { fscanf(fp, "%d", &(cond->resources[r])); } fscanf(fp, "\n"); break; case POSSESSION: fscanf(fp, "%d %d %d\n", &(cond->x), &(cond->y), &(cond->n)); break; default: case_panic("condition type", cond->type); break; } } if (Debug) printf("... Done reading globals.\n"); } /* Read in the entire sides section. Sides have several levels of detail, */ /* since sides' views of the world can be pretty large and we don't always */ /* need to remember them. */ int sidedetail; read_sides(fp) FILE *fp; { int numtoread, detail, extension, i; Side *side; curgiven = 0; fscanf(fp, "Sides %d %d %d\n", &numtoread, &detail, &extension); if (detail < 0 || detail > SIDEALL) case_panic("detail", detail); sidedetail = detail; if (Debug) printf("Will try to read %d sides at detail %d ...\n", numtoread, detail); for (i = 0; i < numtoread; ++i) { if (detail >= SIDEBASIC) side = read_basic_side(fp); if (side != NULL) { if (detail >= SIDESLOTS) read_side_attributes(side, numtoread, fp); if (detail >= SIDEVIEW) read_side_view(side, fp); if (detail >= SIDEMISC) { read_side_misc(side, fp); read_side_statistics(side, fp); } if (Debug) printf("Got side named \"%s\",\n", side->name); } } if (Debug) printf("... Done reading sides.\n"); return detail; } /* Create a side, which only requires a name - person-ness and display come */ /* from the command line if any were supplied. */ /* Somebody should react coherently to side creation failure... */ Side * read_basic_side(fp) FILE *fp; { bool human = FALSE; char *host = NULL; int j; fscanf(fp, "%s\n", tmpbuf); for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == '*') tmpbuf[j] = ' '; if (curgiven < numgivens) { human = humans[curgiven]; host = hosts[curgiven]; ++curgiven; } return create_side(tmpbuf, human, host); } /* Read the most important attributes of a side. */ read_side_attributes(side, numtoread, fp) Side *side; int numtoread; FILE *fp; { int s, u; fscanf(fp, "%hd", &(side->ideology)); for (s = 0; s < numtoread; ++s) fscanf(fp, "%hd", &(side->attitude[s])); for_all_unit_types(u) fscanf(fp, "%hd", &(side->counts[u])); fscanf(fp, "\n"); } /* Read the goriest of details about a side - those things that are */ /* relevant only to a particular game. */ read_side_misc(side, fp) Side *side; FILE *fp; { bool human; char host[BUFSIZE]; fscanf(fp, "%d %s %hd ", &human, host, &(side->lost)); fscanf(fp, "\n"); } /* Read the performance statistics of a side. */ read_side_statistics(side, fp) Side *side; FILE *fp; { int u, u2, i; for_all_unit_types(u) { for (i = 0; i < NUMREASONS; ++i) { fscanf(fp, "%hd", &(side->balance[u][i])); } fscanf(fp, "\n"); for_all_unit_types(u2) { fscanf(fp, "%hd", &(side->atkstats[u][u2])); } fscanf(fp, "\n"); for_all_unit_types(u2) { fscanf(fp, "%hd", &(side->hitstats[u][u2])); } fscanf(fp, "\n"); } } /* Read about what has been seen in the world. */ read_side_view(side, fp) Side *side; FILE *fp; { char ch1, ch2; int x, y, view; for (y = 0; y < world.height; ++y) { for (x = 0; x < world.width; ++x) { ch1 = getc(fp); ch2 = getc(fp); if (ch1 == '?' && ch2 == '?') view = UNSEEN; else if (ch1 == '.' && ch2 == '.') view = EMPTY; else view = buildview(ch2 - '0', find_unit_char(ch1)); set_side_view(side, x, y, view); } fscanf(fp, "\n"); } } /* Read the entire units section. Units also have different levels of */ /* detail. */ read_units(fp) FILE *fp; { int numtoread, detail, extension, i; Unit *unit, *transport; fscanf(fp, "Units %d %d %d\n", &numtoread, &detail, &extension); if (detail < 0 || detail > UNITALL) case_panic("detail", detail); if (Debug) printf("Will try to restore %d units at detail %d ...\n", numtoread, detail); for (i = 0; i < numtoread; ++i) { nut = -1; if (detail >= UNITBASIC) { if ((unit = read_basic_unit(fp)) != NULL) { if (detail >= UNITSLOTS) read_unit_attributes(unit, fp); if (detail >= UNITORDERS) read_unit_orders(unit, fp); if (nusn >= 0) { unit_changes_side(unit, side_n(nusn), -1, -1); } if (nut >= 0) { transport = find_unit(nut); if (transport != NULL) { occupy_unit(unit, transport); } else { occupy_hex(unit, nux, nuy); } } else { occupy_hex(unit, nux, nuy); } if (Debug) printf("Got %s,\n", unit_handle(NULL, unit)); } } } if (Debug) printf("... Done reading units.\n"); return detail; } /* Read the barest info about a neutral unit - just type, name, and loc. */ /* Do weird stuff to handle empty names represented by "*". */ /* Give it full supplies (good idea?). Can't place just yet, because it */ /* might actually be an occupant of something else. */ Unit * read_basic_unit(fp) FILE *fp; { char ch; int j, u; Unit *newunit; fscanf(fp, "%c %s %d,%d %d\n", &ch, tmpbuf, &nux, &nuy, &nusn); for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == '*') tmpbuf[j] = ' '; if (strcmp(tmpbuf, " ") == 0) strcpy(tmpbuf, ""); if ((u = find_unit_char(ch)) != NOTHING) { if ((newunit = create_unit(u, tmpbuf)) != NULL) { init_supply(newunit); } } else { fprintf(stderr, "Unit '%c' is not of this period!\n", ch); exit(1); } return newunit; } /* Read most of a unit's attributes, but not orders and suchlike. */ read_unit_attributes(unit, fp) Unit *unit; FILE *fp; { int truesidenum, r; fscanf(fp, "%hd %hd %d %hd %hd %hd %hd %hd %hd %hd %d", &(unit->id), &(unit->number), &truesidenum, &(unit->hp), &(unit->quality), &(unit->morale), &(unit->fatigue), &(unit->product), &(unit->schedule), &(unit->built), &nut); for_all_resource_types(r) { fscanf(fp, "%hd", &(unit->supply[r])); } fscanf(fp, "\n"); unit->trueside = side_n(truesidenum); /* tricky way to ensure subsequent units will have unique ids */ nextid = max(nextid, unit->id + 1); } /* Read everything back in and try to recreate the unit's orders exactly. */ /* The saved orders include a flag for the presence of standing orders. */ read_unit_orders(unit, fp) Unit *unit; FILE *fp; { int i, more, uord[MAXUTYPES]; fscanf(fp, "%hd %hd", &(unit->lastdir), &(unit->awake)); read_one_order(fp, &(unit->orders)); fscanf(fp, "%d\n", &more); if (more) { unit->standing = (StandingOrder *) malloc(sizeof(StandingOrder)); for_all_unit_types(i) { fscanf(fp, "%d", &(uord[i])); } fscanf(fp, "\n"); for_all_unit_types(i) { if (uord[i]) { unit->standing->orders[i] = (Order *) malloc(sizeof(Order)); read_one_order(fp, unit->standing->orders[i]); fscanf(fp, "\n"); } } } } /* The exact way to read an order depends on what type it is. */ read_one_order(fp, order) FILE *fp; Order *order; { int leadernum; fscanf(fp, "%hd %hd %hd", &(order->type), &(order->rept), &(order->flags)); switch (orderargs[order->type]) { case NOARG: break; case DIR: fscanf(fp, "%hd", &(order->p.dir)); break; case POS: fscanf(fp, "%hd,%hd", &(order->p.pt[0].x), &(order->p.pt[0].y)); break; case LEADER: fscanf(fp, "%d", &leadernum); /* should finish this */ break; case WAYPOINTS: fscanf(fp, "%hd,%hd %hd,%hd", &(order->p.pt[0].x), &(order->p.pt[0].y), &(order->p.pt[1].x), &(order->p.pt[1].y)); break; default: case_panic("order arg type", orderargs[order->type]); break; } } /* Output is quite similar to input of mapfiles, but of course everything */ /* is reversed. */ /* A savefile has a particular format; it includes all sections except */ /* period, which may or may not be referenced as an included file. */ /* It is important that this routine not attempt to use graphics, since it */ /* may be called when graphics code fails. */ write_savefile(fname) char *fname; { Header *head; head = (Header *) malloc(sizeof(Header)); head->numincls = (perfilename ? 1 : 0); head->includes[0] = perfilename; head->sections = "+-++++"; head->numnotes = 0; head->sdetail = SIDEALL; head->udetail = UNITALL; return write_mapfile(fname, head); } /* A scenario is considerably more variable than a savefile, but the */ /* principle is the same. */ write_scenario(fname, sections, sdetail, udetail) char *fname, *sections; int sdetail, udetail; { int i; Header *head; head = (Header *) malloc(sizeof(Header)); head->numincls = head->numnotes = 0; if (mainhead) { head->numnotes = mainhead->numnotes; for (i = 0; i < head->numnotes; ++i) { head->notes[i] = mainhead->notes[i]; } head->numincls = mainhead->numincls; for (i = 0; i < head->numincls; ++i) { head->includes[i] = mainhead->includes[i]; } } head->sections = copy_string(sections); head->sdetail = sdetail; head->udetail = udetail; return write_mapfile(fname, head); } /* Given a file name and a header telling what to put in, do the putting. */ /* Returns true if file opened successfully. */ write_mapfile(fname, head) char *fname; Header *head; { int i; FILE *fp; if ((fp = fopen(fname, "w")) != NULL) { fprintf(fp, "Xconq %d %s %s%s\n", head->numincls, head->sections, (head->numnotes > 0 ? head->notes[0] : ""), (head->numnotes > 1 ? ";" : "")); if (head->numnotes > 1) { for (i = 1; i < head->numnotes; ++i) { fprintf(fp, "%s\n", head->notes[i]); } fprintf(fp, ".\n"); } for (i = 0; i < head->numincls; ++i) { fprintf(fp, "%s\n", head->includes[i]); } if (vsect(head->sections)) write_version(fp); /* no period writing */ if (msect(head->sections)) write_map(fp); if (gsect(head->sections)) write_globals(fp); if (ssect(head->sections)) write_sides(fp, head->sdetail); if (usect(head->sections)) write_units(fp, head->udetail); fclose(fp); return TRUE; } else { return FALSE; } } /* Writing out the version is pretty easy. */ write_version(fp) FILE *fp; { fprintf(fp, "%s\n", version); } /* Write the map section. Try to find runs of the same type and make a more */ /* compact output. */ write_map(fp) FILE *fp; { int extension = 0, x, y, run, runterr, terr, i; fprintf(fp, "Map %d %d %d %d %d\n", world.width, world.height, world.scale, world.known, extension); for (y = world.height-1; y >= 0; --y) { run = 0; runterr = terrain_at(0, y); for (x = 0; x < world.width; ++x) { terr = terrain_at(x, y); if (terr == runterr) { run++; } else { if (run >= 3) { fprintf(fp, "%d%c", run, ttypes[runterr].tchar); } else { for (i = 0; i < run; ++i) putc(ttypes[runterr].tchar, fp); } runterr = terr; run = 1; } } fprintf(fp, "%d%c\n", run, ttypes[terr].tchar); } } /* Write the globals section, which mostly consists of win/lose conditions. */ write_globals(fp) FILE *fp; { int extension = 0, i, u, r; Condition *cond; fprintf(fp, "Globals %d %d %d %d %d %d\n", global.time, global.endtime, global.setproduct, global.leavemap, global.numconds, extension); for (i = 0; i < global.numconds; ++i) { cond = &(global.winlose[i]); fprintf(fp, "%d %d %d %d %d\n", cond->win, cond->type, cond->starttime, cond->endtime, cond->sidesn); switch (cond->type) { case TERRITORY: fprintf(fp, "%d\n", cond->n); break; case UNITCOUNT: for_all_unit_types(u) { fprintf(fp, "%d ", cond->units[u]); } fprintf(fp, "\n"); break; case RESOURCECOUNT: for_all_resource_types(r) { fprintf(fp, "%d ", cond->resources[r]); } fprintf(fp, "\n"); break; case POSSESSION: fprintf(fp, "%d %d %d\n", cond->x, cond->y, cond->n); break; default: case_panic("condition type", cond->type); break; } } } /* Write the sides section, at the given level of detail. */ write_sides(fp, detail) FILE *fp; int detail; { int extension = 0; Side *side; fprintf(fp, "Sides %d %d %d\n", numsides, detail, extension); if (detail < 0 || detail > SIDEALL) case_panic("detail", detail); if (Debug) printf("Will try to write %d sides at detail %d ...\n", numsides, detail); for_all_sides(side) { if (detail >= SIDEBASIC) write_basic_side(side, fp); if (detail >= SIDESLOTS) write_side_attributes(side, fp); if (detail >= SIDEVIEW) write_side_view(side, fp); if (detail >= SIDEMISC) { write_side_misc(side, fp); write_side_statistics(side, fp); } if (Debug) printf("Wrote side \"%s\",\n", side->name); } if (Debug) printf("... Done writing sides.\n"); } /* A side can be saved with or without the entire view, which is a fairly */ /* large sort of thing. Machine sides without displays have their names */ /* written as "*" - let us hope and pray that such a perverted name never */ /* appears as a valid X host/display name. */ write_basic_side(side, fp) Side *side; FILE *fp; { int j; strcpy(tmpbuf, side->name); for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == ' ') tmpbuf[j] = '*'; fprintf(fp, "%s\n", tmpbuf); } /* Write the random but important attributes of a side. */ write_side_attributes(side, fp) Side *side; FILE *fp; { int u, s; fprintf(fp, "%d ", side->ideology); for (s = 0; s < numsides; ++s) fprintf(fp, "%d ", side->attitude[s]); fprintf(fp, " "); for_all_unit_types(u) fprintf(fp, "%d ", side->counts[u]); fprintf(fp, "\n"); } /* Write about what has been seen in the world. (should be more compact) */ write_side_view(side, fp) Side *side; FILE *fp; { char ch1, ch2; int x, y, view; for (y = 0; y < world.height; ++y) { for (x = 0; x < world.width; ++x) { view = side_view(side, x, y); if (view == UNSEEN) ch1 = ch2 = '?'; else if (view == EMPTY) ch1 = ch2 = '.'; else { ch1 = utypes[vtype(view)].uchar; ch2 = vside(view) + '0'; } putc(ch1, fp); putc(ch2, fp); } fprintf(fp, "\n"); } } /* More volatile things, generally only of interest for saved games. */ write_side_misc(side, fp) Side *side; FILE *fp; { fprintf(fp, "%d %s %d \n", side->humanp, (side->host ? side->host : "*"), side->lost, side->timeleft); } /* Write the performance statistics of a side. (the unit record may be */ /* crucial to deciding win/lose conditions.) */ write_side_statistics(side, fp) Side *side; FILE *fp; { int u, u2, i; for_all_unit_types(u) { for (i = 0; i < NUMREASONS; ++i) { fprintf(fp, "%d ", side->balance[u][i]); } fprintf(fp, "\n"); for_all_unit_types(u2) { fprintf(fp, "%d ", side->atkstats[u][u2]); } fprintf(fp, "\n"); for_all_unit_types(u2) { fprintf(fp, "%d ", side->hitstats[u][u2]); } fprintf(fp, "\n"); } } /* Write the unit section of a mapfile. Each level of detail has its own */ /* line, while standing orders will take one line per unit type. */ /* Get rid of dead units and sort everything, so as to make sure that */ /* transports are always written before occupants. */ write_units(fp, detail) FILE *fp; int detail; { int extension = 0; Unit *unit; flush_dead_units(); sort_units(); if (detail < 0 || detail > UNITALL) case_panic("detail", detail); fprintf(fp, "Units %d %d %d\n", numunits, detail, extension); if (Debug) printf("Writing %d units at detail %d ...\n", numunits, detail); if (detail >= UNITBASIC) { for_all_units(unit) { if (detail >= UNITBASIC) write_basic_unit(unit, fp); if (detail >= UNITSLOTS) write_unit_attributes(unit, fp); if (detail >= UNITORDERS) write_unit_orders(unit, fp); if (Debug) printf("Wrote %s,\n", unit_handle(NULL, unit)); } } if (Debug) printf("... Done writing units.\n"); } /* Write only the minimal info about a unit - type, name, and position. */ /* To make scanf happy, spaces in the name are replaced with stars. */ /* Fortunately, names never seem to have stars in them. */ /* Unnamed units get a star all by itself in the name position. */ write_basic_unit(unit, fp) Unit *unit; FILE *fp; { int j; if (unit->name == NULL || strlen(unit->name) < 1) { sprintf(tmpbuf, "*"); } else { strcpy(tmpbuf, unit->name); for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == ' ') tmpbuf[j] = '*'; } fprintf(fp, "%c %s %d,%d %d\n", utypes[unit->type].uchar, tmpbuf, unit->x, unit->y, side_number(unit->side)); } /* Write the most interesting attributes of a unit. Just a long list of */ /* numbers, no special tricks needed. */ write_unit_attributes(unit, fp) Unit *unit; FILE *fp; { int i; fprintf(fp, "%d %d %d %d %d %d %d %d %d %d %d ", unit->id, unit->number, side_number(unit->trueside), unit->hp, unit->quality, unit->morale, unit->fatigue, unit->product, unit->schedule, unit->built, (unit->transport ? unit->transport->id : -1)); for_all_resource_types(i) { fprintf(fp, "%d ", unit->supply[i]); } fprintf(fp, "\n"); } /* Write the unit's orders and standing orders out. */ /* This is usually for game saving, although I suppose it has other uses. */ write_unit_orders(unit, fp) Unit *unit; FILE *fp; { int i; fprintf(fp, "%d %d ", unit->lastdir, unit->awake); write_one_order(fp, &(unit->orders)); fprintf(fp, " %d\n", (unit->standing != NULL)); if (unit->standing != NULL) { for_all_unit_types(i) { fprintf(fp, "%d ", (unit->standing->orders[i] != NULL)); } fprintf(fp, "\n"); for_all_unit_types(i) { if (unit->standing->orders[i] != NULL) { write_one_order(fp, unit->standing->orders[i]); fprintf(fp, "\n"); } } } } /* Write a single order object, which may be for a unit or a standing order. */ write_one_order(fp, order) FILE *fp; Order *order; { fprintf(fp, " %d %d %d ", order->type, order->rept, order->flags); switch (orderargs[order->type]) { case NOARG: break; case DIR: fprintf(fp, "%d", order->p.dir); break; case POS: fprintf(fp, "%d,%d", order->p.pt[0].x, order->p.pt[0].y); break; case LEADER: fprintf(fp, "%d", order->p.leader->id); break; case WAYPOINTS: fprintf(fp, "%d,%d %d,%d", order->p.pt[0].x, order->p.pt[0].y, order->p.pt[1].x, order->p.pt[1].y); break; default: case_panic("order arg type", orderargs[order->type]); break; } } /* Display the mapfile header info. */ describe_mapfiles(side) Side *side; { int i; wprintf(side, "The world is %d hexes around by %d hexes high.", world.width, world.height); wprintf(side, ""); if (mainhead != NULL && mainhead->numnotes > 0) { for (i = 0; i < mainhead->numnotes; ++i) { wprintf(side, "%s", mainhead->notes[i]); } } else { wprintf(side, "(No other documentation is available.)"); } } /* Generalized area search routine. It starts in the immediately adjacent */ /* hexes and expands outwards. The basic structure is to examine successive */ /* "rings" out to the max distance; within each ring, we must scan each of */ /* six faces (picking a random one to start with) by iterating along that */ /* face, in a direction 120 degrees from the direction out to one corner of */ /* the face. Draw a picture if you want to understand it... */ /* Dumb use of "limit" means hexes at edge of map may be searched repeatedly */ /* A good place to optimize things... */ search_area(x0, y0, maxdist, pred, rxp, ryp) int x0, y0, maxdist, (*pred)(), *rxp, *ryp; { int clockwise, dist, dd, d, dir, x1, y1, i, dir2, x, y; maxdist = max(min(maxdist, world.width), min(maxdist, world.height)); clockwise = (flip_coin() ? 1 : -1); for (dist = 1; dist <= maxdist; ++dist) { dd = random_dir(); for_all_directions(d) { dir = (d + dd) % NUMDIRS; x1 = wrap(x0 + dist * dirx[dir]); y1 = limit(y0 + dist * diry[dir]); for (i = 0; i < dist; ++i) { dir2 = opposite_dir(dir + clockwise); x = wrap(x1 + i * dirx[dir2]); y = limit(y1 + i * diry[dir2]); if ((*pred)(x, y)) { *rxp = x; *ryp = y; return TRUE; } } } } return FALSE; } /* Apply a function to every hex within the given radius, being careful (for */ /* both safety and efficiency reasons) not to go past edges. Note that the */ /* distance is inclusive, and that distance of 0 means x0,y0 only. */ /* This routine should be avoided in time-critical code. */ apply_to_area(x0, y0, dist, fn) int x0, y0, dist, (*fn)(); { int x, y, y1 = y0 - dist, y2 = y0 + dist, x1, x2; for (y = y1; y <= y2; ++y) { if (between(0, y, world.height-1)) { x1 = x0 - (y < y0 ? (y - y1) : dist); x2 = x0 + (y > y0 ? (y2 - y) : dist); for (x = x1; x <= x2; ++x) { ((*fn)(wrap(x), y)); } } } }