DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T x

⟦f6915f3eb⟧ TextFile

    Length: 18966 (0x4a16)
    Types: TextFile
    Names: »xconq.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/X/Xconq/xconq.c« 

TextFile

/* 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.");
    }
}