|
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 x
Length: 18966 (0x4a16) Types: TextFile Names: »xconq.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/X/Xconq/xconq.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: xconq.c,v 1.2 88/06/28 10:38:57 shebs Exp $ */ /* Unlike most multi-player network programs, xconq does not use multiple */ /* processes. Instead, it relies on the network capability inherent in */ /* some graphical interfaces (such as X) to open up multiple displays. */ #include "config.h" #include "misc.h" #include "period.h" #include "side.h" #include "unit.h" #include "map.h" #include "global.h" #include "version.h" /* This is for extremely simple menus to be used with novice players. */ typedef struct menu_entry { char *name; char *description; } MenuEntry; extern int nummaps; /* needed to initialize number of maps read */ /* Definitions of globally global variables. */ Global global; /* important (i.e. saved/restored) globals */ MenuEntry mapmenu[MAXMAPMENU]; /* list of mapfiles to suggest to players */ bool givenseen = FALSE; /* true if world known */ bool Debug = FALSE; /* the debugging flag itself. */ bool Build = FALSE; /* magic flag for scenario-builders */ bool Freeze = FALSE; /* keeps machine players from moving */ bool humans[MAXSIDES]; /* flags for which players are human or machine */ char *programname; /* name of the main program */ char *rawfilenames[MAXLOADED]; /* names specified on cmd line */ char version[] = VERSION; /* version string */ char *hosts[MAXSIDES]; /* names of displays for each side */ char spbuf[BUFSIZE]; /* main printing buffer */ char tmpbuf[BUFSIZE]; /* a temp buffer */ int numfiles = 0; /* number of data files asked for */ int numgivens = 0; /* number of sides given on cmd line */ int numhumans = 0; /* number of bona-fide human players */ int givenwidth = DEFAULTWIDTH; /* requested width for a random map */ int givenheight = DEFAULTHEIGHT; /* requested height for a random map */ int giventime = 0; /* requested time for individual turns */ /* Indices of groups of mapfile types mentioned in collection of mapfiles. */ int scnstart, scnend, perstart, perend, mapstart, mapend; Side *winner = NULL; /* the winning side (allies are also winners) */ /* Where it all begins... the main program's primary duty is command line */ /* interpretation, it hands off for everything else. */ main(argc, argv) int argc; char *argv[]; { bool eopt = FALSE, ask = FALSE; char ch; int i, numenemies; programname = argv[0]; nummaps = 0; for (i = 0; i < MAXLOADED; ++i) rawfilenames[i] = ""; add_default_player(); for (i = 1; i < argc; ++i) { if ((argv[i])[0] == '-') { ch = (argv[i])[1]; if (Debug) printf("-%c\n", ch); switch (ch) { case 'A': if (i+1 >= argc) usage_error(); add_player(FALSE, argv[++i]); break; case 'B': Build = TRUE; Freeze = TRUE; break; case 'D': Debug = TRUE; break; case 'e': if (i+1 >= argc) usage_error(); eopt = TRUE; numenemies = atoi(argv[++i]); while (numenemies-- > 0) add_player(FALSE, NULL); break; case 'm': if (i+1 >= argc) usage_error(); make_pathname(NULL, argv[++i], "map", spbuf); rawfilenames[numfiles++] = copy_string(spbuf); break; case 'M': if (i+2 >= argc) usage_error(); givenwidth = atoi(argv[++i]); givenheight = atoi(argv[++i]); break; case 'p': if (i+1 >= argc) usage_error(); make_pathname(NULL, argv[++i], "per", spbuf); rawfilenames[numfiles++] = copy_string(spbuf); break; case 'r': if (numgivens > 1) { fprintf(stderr, "Warning: -r is resetting the list of\n"); fprintf(stderr, "players already given in the command.\n"); } numgivens = numhumans = 0; break; case 's': if (i+1 >= argc) usage_error(); make_pathname(NULL, argv[++i], "scn", spbuf); rawfilenames[numfiles++] = copy_string(spbuf); break; case 't': if (i+1 >= argc) usage_error(); /* Converting to seconds for internal use */ giventime = 60 * atoi(argv[++i]); break; case 'v': givenseen = TRUE; break; case 'x': ask = TRUE; break; default: usage_error(); } } else { /* We just have a host name for a human player */ add_player(TRUE, argv[i]); } } /* Put in a single machine opponent if no -e and no human opponents */ if (!eopt && numgivens == 1) add_player(FALSE, NULL); /* Say hi to the user */ printf("\n Welcome to XCONQ version %s\n\n", version); maybe_dump_news(); /* If no command-line options supplied, work interactively */ if (ask) { if (read_menu_file()) { ask_about_mapfiles(); } else { printf("No period/map/scenario menus available.\n"); } ask_about_players(); } /* While away the hours setting everything up */ init_random(-1); init_sides(); init_units(); init_game(); init_movement(); init_displays(); init_mplayers(); init_sighandlers(); /* Relatively low chance of screwup now, so safe to delete saved game */ if (saved_game()) remove_saved_game(); /* Play! */ play(); } /* Complain and leave if command is garbage. */ usage_error() { fprintf(stderr, "Usage: %s [display] [-A display] [-B] [-e n] [-m name]\n", programname); fprintf(stderr, " [-M w h] [-p name] [-r] [-s name] [-t n] [-v] [-x]\n"); exit(1); } /* Add a player into the array of players, making sure of no overflow. */ /* Fail if so, players probably made a typo on command line - help them */ /* out with a list of what players had been included. */ add_player(ahuman, host) bool ahuman; char *host; { if (numgivens >= MAXSIDES) { fprintf(stderr, "At most %d player sides allowed!\n", MAXSIDES); fprintf(stderr, "(Valid ones were "); list_players(stderr); fprintf(stderr, ")\n"); exit(1); } hosts[numgivens] = host; humans[numgivens] = ahuman; numgivens++; if (ahuman) numhumans++; if (Debug) printf("Added %s player (%s)\n", (ahuman ? "human" : "machine"), (host ? host : "")); } /* The main loop that cycles through the turns and the phases. */ /* Always departs through game end phase (or by core dump :-( ). */ play() { while (TRUE) { init_turn_phase(); spy_phase(); disaster_phase(); build_phase(); supply_phase(); sort_units(); movement_phase(); consumption_phase(); flush_dead_units(); game_end_phase(); } } /* Do random small things to get the turn started, including resetting */ /* some side vars that could get things confused, if set wrong. */ /* Most movement-related things should get set later, at beginning of */ /* move phase. */ init_turn_phase() { Side *side; ++global.time; if (Debug) printf("##### TURN %d #####\n", global.time); for_all_sides(side) { wedge_mode(side); show_timemode(side); if (giventime > 0) update_clock(side); flush_input(side); flush_output(side); } } /* The win/lose phase assesses the sides' performance during that turn, */ /* and can end the game. In fact, it is the only way to get out, except */ /* for the quick exit command. With multiple mixed players, there are a */ /* number of possibilities at game end, such as an all machine win. Also, */ /* it is possible for all players to lose simultaneously! */ game_end_phase() { bool enemiesleft = FALSE; int sidesleft = 0, testrslt; Side *side, *side2, *survivor; if (Debug) printf("Entering game end phase\n"); /* See if any sides have won or lost */ if (global.time >= global.endtime) { notify_all("The end of the world has arrived!"); for_all_sides(side) side_loses(side, NULL); } else if (Build) { /* No winning or losing while building */ } else { for_all_sides(side) { if (!side->lost) { if (side->timedout) { notify_all("The %s ran out of time!", plural_form(side->name)); side_loses(side, NULL); } else if (all_units_gone(side)) { notify_all("The %s have been wiped out!", plural_form(side->name)); side_loses(side, NULL); } else { testrslt = condition_true(side); if (testrslt > 0) side_wins(side); if (testrslt < 0) side_loses(side, NULL); } } } } /* See if any opposing sides left */ for_all_sides(side) { for_all_sides(side2) { if (!side->lost && !side2->lost && enemy_side(side, side2)) enemiesleft = TRUE; } if (!side->lost) { survivor = side; sidesleft++; } } /* Decide if the game is over */ if (numsides == 1) { /* A one-player game can't end */ } else if (sidesleft == 0) { exit_xconq(); } else if (!enemiesleft) { winner = survivor; if (sidesleft == 1) { notify(winner, "You have prevailed over your enemies!"); } else { for_all_sides(side) { if (!side->lost) { notify(side, "Your alliance has defeated its enemies!"); } } } wait_to_exit(winner); exit_xconq(); } else { /* Game isn't over yet */ } } /* Quicky test for absence of all units. */ all_units_gone(side) Side *side; { int u; for_all_unit_types(u) if (side->units[u] > 0) return FALSE; return TRUE; } /* Check out all the winning and losing conditions, returning a flag */ /* on which, if any, are true. The three return values are 1 for win, */ /* -1 for lose, and 0 for undecided. */ condition_true(side) Side *side; { int i, u, r, amt = 0; Unit *unit; Condition *cond; for (i = 0; i < global.numconds; ++i) { cond = &(global.winlose[i]); if ((between(cond->starttime, global.time, cond->endtime)) && (cond->sidesn == -1 || cond->sidesn == side_number(side))) { switch (cond->type) { case TERRITORY: for_all_unit_types(u) { amt += side->units[u] * utypes[u].territory; } if (cond->win) { if (amt >= cond->n) { notify_all("The %s have enough territory to win!", plural_form(side->name)); return 1; } } else { if (amt < cond->n) { notify_all("The %s don't have enough territory!", plural_form(side->name)); return -1; } } break; case UNITCOUNT: if (cond->win) { for_all_unit_types(u) { if (side->units[u] < cond->units[u]) return 0; } notify_all("The %s have enough units to win!", plural_form(side->name)); return 1; } else { for_all_unit_types(u) { if (side->units[u] > cond->units[u]) return 0; } notify_all("The %s don't have the required units!", plural_form(side->name)); return -1; } break; case RESOURCECOUNT: if (cond->win) { for_all_resource_types(r) { if (side->resources[r] < cond->resources[r]) return 0; } notify_all("The %s have enough resources to win!", plural_form(side->name)); return 1; } else { for_all_resource_types(r) { if (side->resources[r] > cond->resources[r]) return 0; } notify_all("The %s don't have the required resources!", plural_form(side->name)); return -1; } break; case POSSESSION: unit = unit_at(cond->x, cond->y); if (cond->win) { if (unit != NULL && unit->side == side) { notify_all("The %s have got hex %d,%d!", plural_form(side->name), cond->x, cond->y); return 1; } } else { if (unit == NULL || unit->side != side) { notify_all("The %s don't have hex %d,%d!", plural_form(side->name), cond->x, cond->y); return -1; } } break; default: case_panic("condition type", cond->type); break; } } } return 0; } /* Winning is defined as not losing :-) */ side_wins(side) Side *side; { Side *side2; for_all_sides(side2) { if (!allied_side(side, side2)) side_loses(side2, NULL); } } /* Be rude to losers and give all their units to their defeaters (might */ /* not be one, so units become neutral or dead). Give the loser a chance */ /* to study the screen, mark the side as lost, then pass on this detail to */ /* all the other sides and remove spurious leftover images of units. */ side_loses(side, victor) Side *side, *victor; { int ux, uy; Unit *unit; Side *side2; notify(side, "You lost, %s!", (flip_coin() ? "sucker" : "turkey")); if (active_display(side)) { wait_to_exit(side); close_display(side); wedge_mode(side); } for_all_units(unit) { if (alive(unit) && unit->side == side) { ux = unit->x; uy = unit->y; unit_changes_side(unit, victor, CAPTURE, SURRENDER); all_see_hex(ux, uy); } } side->lost = TRUE; for_all_sides(side2) { if (active_display(side2)) { remove_images(side2, side_number(side)); if (side != side2) show_all_sides(side2); } } } /* Leave screen up so everybody can study it, and allow any input to take */ /* it down. OK to freeze all the other sides at once here. */ wait_to_exit(side) Side *side; { if (humanside(side) && active_display(side)) { sprintf(side->promptbuf, "[Do anything to exit]"); show_prompt(side); freeze_wait(side); } } /* This routine should be called before any sort of non-death exit. */ /* Among other things, it updates the statistics. */ exit_xconq() { int n = 0; Unit *unit; Side *side; close_displays(); /* Need to kill off all units to finish up statistics */ for_all_units(unit) kill_unit(unit, (winner ? VICTOR : SURRENDER)); print_statistics(); /* Offer a one-line comment on the game and then leave */ for_all_sides(side) if (!side->lost) n++; switch (n) { case 0: printf("\nEverybody lost!\n\n"); break; case 1: printf("\nThe %s (%s) won!\n\n", plural_form(winner->name), (winner->host ? winner->host : "machine")); break; default: sprintf(spbuf, ""); for_all_sides(side) { if (!side->lost) { if (strlen(spbuf) > 0) { sprintf(tmpbuf, "/"); strcat(spbuf, tmpbuf); } sprintf(tmpbuf, "%s(%s)", side->name, (side->host ? side->host : "")); strcat(spbuf, tmpbuf); } } printf("\nThe %s alliance won!\n\n", spbuf); break; } exit(0); } /* Shut down displays - should be done before any sort of exit. */ close_displays() { Side *side; for_all_sides(side) if (active_display(side)) close_display(side); } /* This is to find out how everybody did. */ print_statistics() { Side *side; FILE *fp; #ifdef STATISTICS if ((fp = fopen(STATSFILE, "w")) != NULL) { for_all_sides(side) { print_side_results(fp, side); print_unit_record(fp, side); print_combat_results(fp, side); if (side->next != NULL) fprintf(fp, "\f\n"); } fclose(fp); } else { fprintf(stderr, "Can't open statistics file \"%s\"\n", STATSFILE); } #endif STATISTICS } /* Read in a file in a special format - basically lines describing mapfiles */ /* with markers for various types in between. Returns true if the file */ /* was actually found. */ read_menu_file() { int i, j; char *tmp; FILE *fp; scnstart = scnend = perstart = perend = mapstart = mapend = 0; make_pathname(XCONQLIB, MAPFILEFILE, NULL, spbuf); if ((fp = fopen(spbuf, "r")) != NULL) { for (i = 0; i < MAXMAPMENU; ++i) { fscanf(fp, "%s", tmpbuf); mapmenu[i].name = copy_string(tmpbuf); tmp = read_line(fp); for (j = 0; tmp[j] != '\0'; ++j) if (tmp[j] != ' ') break; mapmenu[i].description = tmp + j; if (strcmp(mapmenu[i].name, "Scenarios") == 0) { scnstart = i+1; } else if (strcmp(mapmenu[i].name, "Periods") == 0) { scnend = i-1; perstart = i+1; } else if (strcmp(mapmenu[i].name, "Maps") == 0) { perend = i-1; mapstart = i+1; } else if (strcmp(mapmenu[i].name, "End") == 0) { mapend = i-1; break; } } fclose(fp); return TRUE; } else { return FALSE; } } /* Display menus of mapfiles. Player can either choose a single scenario */ /* or a period + map (but no guarantees that map will work with period!) */ ask_about_mapfiles() { char *name; if (file_menu(scnstart, scnend, "scenario", "Enter a number, or hit return to look at maps and periods.", &name)) { make_pathname(NULL, name, "scn", spbuf); rawfilenames[numfiles++] = copy_string(spbuf); } else { if (file_menu(perstart, perend, "historical period", "Enter a number, or hit return for the standard period.", &name)) { make_pathname(NULL, name, "per", spbuf); rawfilenames[numfiles++] = copy_string(spbuf); } else { printf("\nYou will get the standard period.\n"); } if (file_menu(mapstart, mapend, "map", "Enter a number, or hit return for a random map.", &name)) { make_pathname(NULL, name, "map", spbuf); rawfilenames[numfiles++] = copy_string(spbuf); } else { printf("\nYou will get a random map.\n"); } } } /* Given an array of names, return success or failure of choice and maybe */ /* one of the names. */ file_menu(start, end, prompt, help, rslt) int start, end; char *prompt, *help, **rslt; { int i, ans; printf("\n"); if (start > end) return FALSE; for (i = start; i <= end; ++i) { printf("[%d] %-14s %s\n", i - start + 1, mapmenu[i].name, mapmenu[i].description); } while (TRUE) { printf("\nChoose a %s: ", prompt); gets(spbuf); sscanf(spbuf, "%d", &ans); ans += start - 1; if (ans >= start && ans <= end) { *rslt = mapmenu[ans].name; return TRUE; } else if (iindex('?', spbuf) >= 0) { printf("%s\n", help); } else if (strlen(spbuf) == 0) { return FALSE; } else { printf("Garbled answer! Try again...\n"); } } } /* This allows fairly general setup for a collection of players. There */ /* should be facilities for removing as well as adding... */ ask_about_players() { int i, n; while (TRUE) { printf("\nPlayers so far: "); list_players(stdout); printf("\n\nAny others? [number or hostname]: "); gets(spbuf); if (strlen(spbuf) == 0) { return; } else if (strcmp(spbuf, "r") == 0) { numgivens = 0; } else if (iindex('?', spbuf) >= 0) { printf("You can make various combinations of players,\n"); printf("by giving either a number of machine or a host name.\n"); printf("`r' clears the list; hit return when done.\n"); } else if (numgivens < MAXSIDES) { if (sscanf(spbuf, "%d", &n) == 1) { n = max(0, min(n, MAXSIDES - numgivens)); for (i = 0; i < n; ++i) add_player(FALSE, NULL); } else { add_player(TRUE, copy_string(spbuf)); } } else { printf("Can't add anybody else; 'r' starts over.\n"); } } } /* List all the specified players briefly. */ list_players(fp) FILE *fp; { int i; if (numgivens > 0) { fprintf(fp, "%s", (hosts[0] ? hosts[0] : "machine")); for (i = 1; i < numgivens; ++i) { fprintf(fp, ", %s", (hosts[i] ? hosts[i] : "machine")); } } else { fprintf(fp, "no players defined."); } }