empire sources (part 4 of 6)

Charles Simmons chuck at amdahl.uts.amdahl.com
Mon Apr 11 11:00:47 AEST 1988



# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# usermove.c game.c

echo x - usermove.c
sed -e 's/^X//' > "usermove.c" << '//E*O*F usermove.c//'
X/* %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */
X
X/*
X *    Copyright (C) 1987, 1988 Chuck Simmons
X * 
X * See the file COPYING, distributed with empire, for restriction
X * and warranty information.
X */
X
X/*
Xusermove.c -- Let the user move her troops.
X*/
X
X#include <curses.h>
X#include <ctype.h>
X#include "empire.h"
X#include "extern.h"
X
Xvoid
Xuser_move () {
X	void piece_move();
X
X	int i, j, sec, sec_start;
X	piece_info_t *obj, *next_obj;
X	int prod;
X
X	/* First we loop through objects to update the user's view
X	of the world and perform any other necessary processing.
X	We would like to have the world view up to date before
X	asking the user any questions.  This means that we should
X	also scan through all cities before possibly asking the
X	user what to produce in each city. */
X
X	for (i = 0; i < NUM_OBJECTS; i++)
X	for (obj = user_obj[i]; obj != NULL; obj = obj->piece_link.next) {
X		obj->moved = 0; /* nothing moved yet */
X		scan (user_map, obj->loc); /* refresh user's view of world */
X	}
X
X	/* produce new hardware */
X	for (i = 0; i < NUM_CITY; i++)
X	    if (city[i].owner == USER) {
X		scan (user_map, city[i].loc);
X		prod = city[i].prod;
X
X		if (prod == NOPIECE) { /* need production? */
X			set_prod (&(city[i])); /* ask user what to produce */
X		}
X		else if (city[i].work++ >= (long)piece_attr[prod].build_time) {
X			comment ("City at %d has completed %s.",
X				city[i].loc, piece_attr[prod].article);
X
X			produce (&city[i]);
X			/* produce should set object.moved to 0 */
X		}
X	}
X
X	/* move all satellites */
X	for (obj = user_obj[SATELLITE]; obj != NULL; obj = next_obj) {
X		next_obj = obj->piece_link.next;
X		move_sat (obj);
X	}
X	
X	sec_start = cur_sector (); /* get currently displayed sector */
X	if (sec_start == -1) sec_start = 0;
X
X	/* loop through sectors, moving every piece in the sector */
X	for (i = sec_start; i < sec_start + NUM_SECTORS; i++) {
X		sec = i % NUM_SECTORS;
X		sector_change (); /* allow screen to be redrawn */
X
X		for (j = 0; j < NUM_OBJECTS; j++) /* loop through obj lists */
X		for (obj = user_obj[move_order[j]]; obj != NULL;
X			obj = next_obj) { /* loop through objs in list */
X			next_obj = obj->piece_link.next;
X
X			if (!obj->moved) /* object not moved yet? */
X			if (loc_sector (obj->loc) == sec) /* object in sector? */
X			piece_move (obj); /* yup; move the object */
X		}
X		if (cur_sector () == sec) { /* is sector displayed? */
X			print_sector_u (sec); /* make screen up-to-date */
X			(void) refresh (); /* show it to the user */
X		}
X	}
X	if (save_movie) save_movie_screen ();
X}
X
X/*
XMove a piece.  We loop until all the moves of a piece are made.  Within
Xthe loop, we first awaken the piece if it is adjacent to an enemy piece.
XThen we attempt to handle any preprogrammed function for the piece.  If
Xthe piece has not moved after this, we ask the user what to do.
X*/
X
Xvoid
Xpiece_move (obj)
Xpiece_info_t *obj;
X{
X	void move_random(), move_fill(), move_land(), move_explore();
X	void move_path(), move_dir(), move_armyload(), ask_user();
X	void move_armyattack(), move_ttload(), move_repair();
X	void move_transport();
X
X	int changed_loc;
X	int speed, max_hits;
X	int saved_moves;
X	int need_input;
X	long saved_loc;
X	city_info_t *cityp;
X
X	/* set func for piece if on city */
X	cityp = find_city (obj->loc);
X	if (cityp != NULL)
X		if (cityp->func[obj->type] != NOFUNC)
X			obj->func = cityp->func[obj->type];
X
X	changed_loc = FALSE; /* not changed yet */
X	speed = piece_attr[obj->type].speed;
X	max_hits = piece_attr[obj->type].max_hits;
X	need_input = FALSE; /* don't require user input yet */
X
X	while (obj->moved < obj_moves (obj)) {
X		saved_moves = obj->moved; /* save moves made */
X		saved_loc = obj->loc; /* remember starting location */
X
X		if (awake (obj) || need_input){ /* need user input? */
X			ask_user (obj);
X			topini (); /* clear info lines */
X			display_loc_u (obj->loc); /* let user see result */
X			(void) refresh ();
X			need_input = FALSE; /* we got it */
X		}
X		
X		if (obj->moved == saved_moves) /* user set function? */
X		switch (obj->func) { /* handle preprogrammed function */
X		case NOFUNC:    break;
X		case RANDOM:    move_random (obj); break;
X		case SENTRY:    obj->moved = speed; break;
X		case FILL:      move_fill (obj); break;
X		case LAND:      move_land (obj); break;
X		case EXPLORE:   move_explore (obj); break;
X		case ARMYLOAD:  move_armyload (obj); break;
X		case ARMYATTACK:move_armyattack (obj); break;
X		case TTLOAD:    move_ttload (obj); break;
X		case REPAIR:    move_repair (obj); break;
X		case WFTRANSPORT: move_transport (obj); break;
X
X		case MOVE_N:
X		case MOVE_NE:
X		case MOVE_E:
X		case MOVE_SE:
X		case MOVE_S:
X		case MOVE_SW:
X		case MOVE_W:
X		case MOVE_NW:
X			move_dir (obj); break;
X
X		default: move_path (obj); break;
X		}
X
X		if (obj->moved == saved_moves) need_input = TRUE;
X		
X		/* handle fighters specially.  If in a city or carrier, turn
X		is over and reset range to max.  Otherwise, if
X		range = 0, fighter crashes and burns and turn is over. */
X
X		if (obj->type == FIGHTER && obj->hits > 0) {
X			if ((user_map[obj->loc].contents == 'O'
X			  || user_map[obj->loc].contents == 'C')
X			&& obj->moved > 0) {
X				obj->range = piece_attr[FIGHTER].range;
X				obj->moved = speed;
X				obj->func = NOFUNC;
X				comment ("Landing confirmed.");
X			}
X			else if (obj->range == 0) {
X				comment ("Fighter at %d crashed and burned.",
X					obj->loc);
X				kill_obj (obj, obj->loc);
X			}
X		}
X
X		if (saved_loc != obj->loc) changed_loc = TRUE;
X	}
X	/* if a boat is in port, damaged, and never moved, fix some damage */
X	if (obj->hits > 0 /* still alive? */
X		&& !changed_loc /* object never changed location? */
X		&& obj->type != ARMY && obj->type != FIGHTER /* it is a boat? */
X		&& obj->hits < max_hits /* it is damaged? */
X		&& user_map[obj->loc].contents == 'O') /* it is in port? */
X	obj->hits++; /* fix some damage */
X}
X
X/*
XMove a piece at random.  We create a list of empty squares to which
Xthe piece can move.  If there are none, we do nothing, otherwise we 
Xmove the piece to a random adjacent square.
X*/
X
Xvoid move_random (obj)
Xpiece_info_t *obj;
X{
X	long loc_list[8];
X	int i, nloc;
X	long loc;
X
X	nloc = 0;
X
X	for (i = 0; i < 8; i++) {
X		loc = obj->loc + dir_offset[i];
X		if (good_loc (obj, loc)) {
X			loc_list[nloc] = loc; /* remember this location */
X			nloc++; /* count locations we can move to */
X		}
X	}
X	if (nloc == 0) return; /* no legal move */
X	i = irand ((long)nloc-1); /* choose random direction */
X	move_obj (obj, loc_list[i]); /* move the piece */
X}
X
X/*
XHave a piece explore.  We look for the nearest unexplored territory
Xwhich the piece can reach and have to piece move toward the
Xterritory.
X*/
X
Xvoid move_explore (obj)
Xpiece_info_t *obj;
X{
X	path_map_t path_map[MAP_SIZE];
X	long loc;
X	char *terrain;
X
X	switch (obj->type) {
X	case ARMY:
X		loc = vmap_find_lobj (path_map, user_map, obj->loc, &user_army);
X		terrain = "+";
X		break;
X	case FIGHTER:
X		loc = vmap_find_aobj (path_map, user_map, obj->loc, &user_fighter);
X		terrain = "+.O";
X		break;
X	default:
X		loc = vmap_find_wobj (path_map, user_map, obj->loc, &user_ship);
X		terrain = ".O";
X		break;
X	}
X	
X	if (loc == obj->loc) return; /* nothing to explore */
X
X	if (user_map[loc].contents == ' ' && path_map[loc].cost == 2)
X		vmap_mark_adjacent (path_map, obj->loc);
X	else vmap_mark_path (path_map, user_map, loc);
X
X	loc = vmap_find_dir (path_map, user_map, obj->loc, terrain, " ");
X	if (loc != obj->loc) move_obj (obj, loc);
X}
X
X/*
XMove an army onto a transport when it arrives.  We scan around the
Xarmy to find a non-full transport.  If one is present, we move the
Xarmy to the transport and waken the army.
X*/
X
Xvoid
Xmove_transport (obj)
Xpiece_info_t *obj;
X{
X	long loc;
X
X	/* look for an adjacent transport */
X	loc = find_transport (USER, obj->loc);
X	
X	if (loc != obj->loc) {
X		move_obj (obj, loc);
X		obj->func = NOFUNC;
X	}
X	else obj->moved = piece_attr[obj->type].speed;
X}
X
X/*
XMove an army toward the nearest loading transport.
XIf there is an adjacent transport, move the army onto
Xthe transport, and awaken the army.
X*/
X
Xstatic view_map_t amap[MAP_SIZE];
X
Xvoid
Xmove_armyload (obj)
Xpiece_info_t *obj;
X{
X	long loc;
X	piece_info_t *p;
X	int i;
X
X	ABORT;
X	
X	/* look for an adjacent transport */
X	loc = find_transport (USER, obj->loc);
X
X	if (loc != obj->loc) {
X		move_obj (obj, loc);
X		obj->func = NOFUNC;
X	}
X	else { /* look for nearest non-full transport */
X		(void) memcpy (amap, user_map, sizeof (view_map_t) * MAP_SIZE);
X
X		/* mark loading transports or cities building transports */
X		for (p = user_obj[TRANSPORT]; p; p = p->piece_link.next)
X		if (p->count < obj_capacity (p)) /* not full? */
X		amap[p->loc].contents = '$';
X		
X		for (i = 0; i < NUM_CITY; i++)
X		if (city[i].owner == USER && city[i].prod == TRANSPORT)
X		amap[city[i].loc].contents = '$';
X	}
X}
X		
X/*
XMove an army toward an attackable city or enemy army.
X*/
X
Xvoid
Xmove_armyattack (obj)
Xpiece_info_t *obj;
X{
X	path_map_t path_map[MAP_SIZE];
X	long loc;
X
X	ASSERT (obj->type == ARMY);
X
X	loc = vmap_find_lobj (path_map, user_map, obj->loc, &user_army_attack);
X	
X	if (loc == obj->loc) return; /* nothing to attack */
X
X	vmap_mark_path (path_map, user_map, loc);
X
X	loc = vmap_find_dir (path_map, user_map, obj->loc, "+", "X*a");
X	if (loc != obj->loc) move_obj (obj, loc);
X}
X
Xvoid
Xmove_ttload (obj)
Xpiece_info_t *obj;
X{
X	ABORT;
X	obj = obj;
X}
X
X/*
XMove a ship toward port.  If the ship is healthy, wake it up.
X*/
X
Xvoid
Xmove_repair (obj)
Xpiece_info_t *obj;
X{
X	path_map_t path_map[MAP_SIZE];
X	long loc;
X
X	ASSERT (obj->type > FIGHTER);
X	
X	if (obj->hits == piece_attr[obj->type].max_hits) {
X		obj->func = NOFUNC;
X		return;
X	}
X	
X	if (user_map[obj->loc].contents == 'O') { /* it is in port? */
X		obj->moved += 1;
X		return;
X	}
X
X	loc = vmap_find_wobj (path_map, user_map, obj->loc, &user_ship_repair);
X	
X	if (loc == obj->loc) return; /* no reachable city */
X
X	vmap_mark_path (path_map, user_map, loc);
X
X	/* try to be next to ocean to avoid enemy pieces */
X	loc = vmap_find_dir (path_map, user_map, obj->loc, ".O", ".");
X	if (loc != obj->loc) move_obj (obj, loc);
X}
X
X/*
XHere we have a transport or carrier waiting to be filled.  If the
Xobject is not full, we set the move count to its maximum value.
XOtherwise we awaken the object.
X*/
X
Xvoid move_fill (obj)
Xpiece_info_t *obj;
X{
X	if (obj->count == obj_capacity (obj)) /* full? */
X		obj->func = NOFUNC; /* awaken full boat */
X	else obj->moved = piece_attr[obj->type].speed;
X}
X
X/*
XHere we have a piece that wants to land at the nearest carrier or
Xowned city.  We scan through the lists of cities and carriers looking
Xfor the closest one.  We then move toward that item's location.
XThe nearest landing field must be within the object's range.
X*/
X
Xvoid
Xmove_land (obj)
Xpiece_info_t *obj;
X{
X	void move_to_dest();
X
X	long best_dist, best_loc;
X	long new_dist;
X	piece_info_t *p;
X
X	best_dist = find_nearest_city (obj->loc, USER, &best_loc);
X
X	for (p = user_obj[CARRIER]; p != NULL; p = p->piece_link.next) {
X		new_dist = dist (obj->loc, p->loc);
X		if (new_dist < best_dist) {
X			best_dist = new_dist;
X			best_loc = p->loc;
X		}
X	}
X	if (best_dist == 0) obj->moved += 1; /* fighter is on a city */
X	
X	else if (best_dist <= obj->range)
X		move_to_dest (obj, best_loc);
X		
X	else obj->func = NOFUNC; /* can't reach city or carrier */
X}
X
X/*
XMove a piece in the specified direction if possible.
XIf the object is a fighter which has travelled for half its range,
Xwe wake it up.
X*/
X
Xvoid move_dir (obj)
Xpiece_info_t *obj;
X{
X	long loc;
X	int dir;
X
X	dir = MOVE_DIR (obj->func);
X	loc = obj->loc + dir_offset[dir];
X
X	if (good_loc (obj, loc))
X		move_obj (obj, loc);
X}
X
X/*
XMove a piece toward a specified destination if possible.  For each
Xdirection, we see if moving in that direction would bring us closer
Xto our destination, and if there is nothing in the way.  If so, we
Xmove in the first direction we find.
X*/
X
Xvoid move_path (obj)
Xpiece_info_t *obj;
X{
X	if (obj->loc == obj->func)
X		obj->func = NOFUNC;
X	else move_to_dest (obj, obj->func);
X}
X
X/*
XMove a piece toward a specific destination.  We first map out
Xthe paths to the destination, if we can't get there, we return.
XThen we mark the paths to the destination.  Then we choose a
Xmove.
X*/
X
Xvoid move_to_dest (obj, dest)
Xpiece_info_t *obj;
Xlong dest;
X{
X	path_map_t path_map[MAP_SIZE];
X	int fterrain;
X	char *mterrain;
X	long new_loc;
X	
X	switch (obj->type) {
X	case ARMY:
X		fterrain = T_LAND;
X		mterrain = "+";
X		break;
X	case FIGHTER:
X		fterrain = T_AIR;
X		mterrain = "+.O";
X		break;
X	default:
X		fterrain = T_WATER;
X		mterrain = ".O";
X		break;
X	}
X	
X	new_loc = vmap_find_dest (path_map, user_map, obj->loc, dest,
X                                  USER, fterrain);
X	if (new_loc == obj->loc) return; /* can't get there */
X	
X	vmap_mark_path (path_map, user_map, dest);
X	new_loc = vmap_find_dir (path_map, user_map, obj->loc, mterrain, " .");
X	if (new_loc == obj->loc) return; /* can't move ahead */
X	ASSERT (good_loc (obj, new_loc));
X	move_obj (obj, new_loc); /* everything looks good */
X}
X
X/*
XAsk the user to move her piece.
X*/
X
Xvoid ask_user (obj)
Xpiece_info_t *obj;
X{
X	void user_skip(), user_fill(), user_dir(), user_set_dir();
X	void user_wake(), user_set_city_func(), user_cancel_auto();
X	void user_redraw(), user_random(), user_land(), user_sentry();
X	void user_help(), reset_func(), user_explore();
X	void user_build(), user_transport();
X	void user_armyattack(), user_repair();
X
X	char c;
X
X    for (;;) {
X	display_loc_u (obj->loc); /* display piece to move */
X	describe_obj (obj); /* describe object to be moved */
X	display_score (); /* show current score */
X	display_loc_u (obj->loc); /* reposition cursor */
X
X	c = get_chx (); /* get command from user (no echo) */
X	switch (c) {
X	case 'Q': user_dir (obj, NORTHWEST); return;
X	case 'W': user_dir (obj, NORTH); return;
X	case 'E': user_dir (obj, NORTHEAST); return;
X	case 'D': user_dir (obj, EAST); return;
X	case 'C': user_dir (obj, SOUTHEAST); return;
X	case 'X': user_dir (obj, SOUTH); return;
X	case 'Z': user_dir (obj, SOUTHWEST); return;
X	case 'A': user_dir (obj, WEST); return;
X
X	case 'J': edit (obj->loc); reset_func (obj); return;
X	case 'V': user_set_city_func (obj); reset_func (obj); return;
X	
X	case ' ': user_skip (obj); return;
X	case 'F': user_fill (obj); return;
X	case 'I': user_set_dir (obj); return;
X	case 'R': user_random (obj); return;
X	case 'S': user_sentry (obj); return;
X	case 'L': user_land (obj); return;
X	case 'G': user_explore (obj); return;
X	case 'T': user_transport (obj); return;
X	case 'U': user_repair (obj); return;
X	case 'Y': user_armyattack (obj); return;
X
X	case 'B': user_build (obj); break;
X	case 'H': user_help (); break;
X	case 'K': user_wake (obj); break;
X	case 'O': user_cancel_auto (); break;
X	case '\014':
X	case 'P': user_redraw (); break;
X	case '?': describe_obj (obj); break;
X
X	default: (void) beep ();
X	}
X    }
X}
X
X/*
XHere, if the passed object is on a city, we assign
Xthe city's function to the object.  However, we then awaken the
Xobject if necessary because the user only changed the city
Xfunction, and did not tell us what to do with the object.
X*/
X
Xvoid
Xreset_func (obj)
Xpiece_info_t *obj;
X{
X	city_info_t *cityp;
X	
X	cityp = find_city (obj->loc);
X
X	if (cityp != NULL)
X	if (cityp->func[obj->type] != NOFUNC) {
X		obj->func = cityp->func[obj->type];
X		(void) awake (obj);
X	} 
X}
X
X/*
XIncrement the number of moves a piece has used.  If the piece
Xis an army and the army is in a city, move the army to
Xthe city.
X*/
X
Xvoid
Xuser_skip (obj)
Xpiece_info_t *obj;
X{
X	void move_army_to_city();
X
X	if (obj->type == ARMY && user_map[obj->loc].contents == 'O')
X		move_army_to_city (obj, obj->loc);
X	else obj->moved++;
X}
X
X/*
XPut an object in FILL mode.  The object must be either a transport
Xor carrier.  If not, we beep at the user.
X*/
X
Xvoid
Xuser_fill (obj)
Xpiece_info_t *obj;
X{
X	if (obj->type != TRANSPORT && obj->type != CARRIER) (void) beep ();
X	else obj->func = FILL;
X}
X
X/*
XPrint out help information.
X*/
X
Xvoid
Xuser_help () {
X	char c;
X
X	help (help_user, user_lines);
X	prompt ("Press any key to continue: ");
X	c = get_chx ();
X	c = c; /* keep lint happy */
X}
X
X/*
XSet an object's function to move in a certain direction.
X*/
X
Xvoid
Xuser_set_dir (obj)
Xpiece_info_t *obj;
X{
X	char c;
X
X	c = get_chx ();
X	switch (c) {
X	case 'Q': obj->func = MOVE_NW; break;
X	case 'W': obj->func = MOVE_N ; break;
X	case 'E': obj->func = MOVE_NE; break;
X	case 'D': obj->func = MOVE_E ; break;
X	case 'C': obj->func = MOVE_SE; break;
X	case 'X': obj->func = MOVE_S ; break;
X	case 'Z': obj->func = MOVE_SW; break;
X	case 'A': obj->func = MOVE_W ; break;
X	default: (void) beep (); break;
X	}
X}
X
X/*
XWake up the current piece.
X*/
X
Xvoid
Xuser_wake (obj)
Xpiece_info_t *obj;
X{
X	obj->func = NOFUNC;
X}
X
X/*
XSet the piece's func to random.  
X*/
X
Xvoid
Xuser_random (obj)
Xpiece_info_t *obj;
X{
X	obj->func = RANDOM;
X}
X
X/*
XSet a piece's function to sentry.
X*/
X
Xvoid
Xuser_sentry (obj)
Xpiece_info_t *obj;
X{
X	obj->func = SENTRY;
X}
X
X/*
XSet a fighter's function to land at the nearest city.
X*/
X
Xvoid
Xuser_land (obj)
Xpiece_info_t *obj;
X{
X	if (obj->type != FIGHTER) (void) beep ();
X	else obj->func = LAND;
X}
X
X/*
XSet the piece's func to explore.
X*/
X
Xvoid
Xuser_explore (obj)
Xpiece_info_t *obj;
X{
X	obj->func = EXPLORE;
X}
X
X/*
XSet an army's function to WFTRANSPORT.
X*/
X
Xvoid
Xuser_transport (obj)
Xpiece_info_t *obj;
X{
X	if (obj->type != ARMY) (void) beep ();
X	else obj->func = WFTRANSPORT;
X}
X
X/*
XSet an army's function to ARMYATTACK.
X*/
X
Xvoid
Xuser_armyattack (obj)
Xpiece_info_t *obj;
X{
X	if (obj->type != ARMY) (void) beep ();
X	else obj->func = ARMYATTACK;
X}
X
X/*
XSet a ship's function to REPAIR.
X*/
X
Xvoid
Xuser_repair (obj)
Xpiece_info_t *obj;
X{
X	if (obj->type == ARMY || obj->type == FIGHTER) (void) beep ();
X	else obj->func = REPAIR;
X}
X
X/*
XSet a city's function.
X*/
X
Xvoid
Xuser_set_city_func (obj)
Xpiece_info_t *obj;
X{
X	void e_city_fill(), e_city_explore(), e_city_stasis();
X	void e_city_wake(), e_city_random(), e_city_repair();
X	void e_city_attack();
X	
X	int type;
X	char e;
X	city_info_t *cityp;
X
X	cityp = find_city (obj->loc);
X	if (!cityp || cityp->owner != USER) {
X		(void) beep ();
X		return;
X	}
X
X	type = get_piece_name();
X	if (type == NOPIECE) {
X		(void) beep ();
X		return;
X	}
X	
X	e = get_chx ();
X	
X	switch (e) {
X	case 'F': /* fill */
X		e_city_fill (cityp, type);
X		break;
X	case 'G': /* explore */
X		e_city_explore (cityp, type);
X		break;
X	case 'I': /* directional stasis */
X		e_city_stasis (cityp, type);
X		break;
X	case 'K': /* turn off function */
X		e_city_wake (cityp, type);
X		break;
X	case 'R': /* make piece move randomly */
X		e_city_random (cityp, type);
X		break;
X	case 'U': /* repair ship */
X		e_city_repair (cityp, type);
X		break;
X	case 'Y': /* set army func to attack */
X		e_city_attack (cityp, type);
X		break;
X	default: /* bad command? */
X		(void) beep ();
X		break;
X	}
X}
X
X/*
XChange a city's production.
X*/
X
Xvoid
Xuser_build (obj)
Xpiece_info_t *obj;
X{
X	city_info_t *cityp;
X
X	if (user_map[obj->loc].contents != 'O') { /* no user city here? */
X		(void) beep ();
X		return;
X	}
X	cityp = find_city (obj->loc);
X	ASSERT (cityp != NULL);
X	set_prod (cityp);
X}
X
X/*
XMove a piece in the direction specified by the user.
XThis routine handles attacking objects.
X*/
X
Xvoid
Xuser_dir (obj, dir)
Xpiece_info_t *obj;
Xint dir;
X{
X	void user_dir_army(), user_dir_fighter(), user_dir_ship();
X
X	long loc;
X
X	loc = obj->loc + dir_offset[dir];
X
X	if (good_loc (obj, loc)) {
X		move_obj (obj, loc);
X		return;
X	}
X	if (!map[loc].on_board) {
X		error ("You cannot move to the edge of the world.");
X		delay ();
X		return;
X	}
X	switch (obj->type) {
X	case ARMY: user_dir_army (obj, loc); break;
X	case FIGHTER: user_dir_fighter (obj, loc); break;
X	default: user_dir_ship (obj, loc); break;
X	}
X}
X
X/*
XWe have an army that wants to attack something or move onto some
Xunreasonable terrain.  We check for errors, question the user if
Xnecessary, and attack if necessary.
X*/
X
Xvoid
Xuser_dir_army (obj, loc)
Xpiece_info_t *obj;
Xlong loc;
X{
X	void fatal();
X	
X	int enemy_killed;
X	
X	enemy_killed = FALSE;
X
X	if (user_map[loc].contents == 'O') /* attacking own city */
X		move_army_to_city (obj, loc);
X
X	else if (user_map[loc].contents == 'T') /* transport full? */
X		fatal (obj, loc,
X	"Sorry, sir.  There is no more room on the transport.  Do you insist? ",
X	"Your army jumped into the briny and drowned.");
X
X	else if (map[loc].contents == '.') { /* going for a swim? */
X		if (!getyn ( /* thanks to Craig Hansen for this next message */
X	"Troops can't walk on water, sir.  Do you really want to go to sea? "))
X		return;
X
X		if (user_map[obj->loc].contents == 'T')
X			comment ("Your army jumped into the briny and drowned.");
X
X		else if (user_map[loc].contents == '.')
X			comment ("Your army marched dutifully into the sea and drowned.");
X
X		else { /* attack something at sea */
X			enemy_killed = islower (user_map[loc].contents);
X			attack (obj, loc);
X	
X			if (obj->hits > 0) /* ship won? */
X			comment ("Your army regretfully drowns after its successful assault.");
X		}
X		if (obj->hits > 0) {
X			kill_obj (obj, loc);
X			if (enemy_killed) scan (comp_map, loc);
X		}
X	}
X		
X	else if (isupper (user_map[loc].contents)
X		&& user_map[loc].contents != 'X') { /* attacking self */
X		if (!getyn (
X	"Sir, those are our men!  Do you really want to attack them? "))
X		return;
X
X		attack (obj, loc);
X	}
X
X	else attack (obj, loc);
X}
X
X/*
XHere we have a fighter wanting to attack something.  There are only
Xthree cases:  attacking a city, attacking ourself, attacking the enemy.
X*/
X
Xvoid
Xuser_dir_fighter (obj, loc)
Xpiece_info_t *obj;
Xlong loc;
X{
X	if (map[loc].contents == '*')
X		fatal (obj, loc,
X	"That's never worked before, sir.  Do you really want to try? ",
X	"Your fighter was shot down.");
X
X	else if (isupper (user_map[loc].contents)) {
X		if (!getyn (
X	"Sir, those are our men!  Do you really want to attack them? "))
X		return;
X
X		attack (obj, loc);
X	}
X
X	else attack (obj, loc);
X}
X
X/*
XHere we have a ship attacking something, or trying to move on
Xshore.  Our cases are: moving ashore (and subcases), attacking
Xa city, attacking self, attacking enemy.
X*/
X	
Xvoid
Xuser_dir_ship (obj, loc)
Xpiece_info_t *obj;
Xlong loc;
X{
X	int enemy_killed;
X
X	enemy_killed = FALSE;
X
X	if (map[loc].contents == '*') {
X		(void) sprintf (jnkbuf, "Your %s broke up on shore.",
X				piece_attr[obj->type].name);
X
X		fatal (obj, loc,
X	"That's never worked before, sir.  Do you really want to try? ",
X			jnkbuf);
X	}
X
X	else if (map[loc].contents == '+') { /* moving ashore? */
X		if (!getyn (
X	"Ships need sea to float, sir.  Do you really want to go ashore? "))
X		return;
X
X		if (user_map[loc].contents == '+')
X			comment ("Your %s broke up on shore.",
X				 piece_attr[obj->type].name);
X
X		else { /* attack something on shore */
X			enemy_killed = islower (user_map[loc].contents);
X			attack (obj, loc);
X
X			if (obj->hits > 0) /* ship won? */
X				comment ("Your %s breaks up after its successful assault.",
X					 piece_attr[obj->type].name);
X		}
X		if (obj->hits > 0) {
X			kill_obj (obj, loc);
X			if (enemy_killed) scan (comp_map, loc);
X		}
X	}
X		
X	else if (isupper (user_map[loc].contents)) { /* attacking self */
X		if (!getyn (
X	"Sir, those are our men!  Do you really want to attack them? "))
X		return;
X
X		attack (obj, loc);
X	}
X
X	else attack (obj, loc);
X}
X
X/*
XHere a user wants to move an army to a city.  If the city contains
Xa non-full transport, we make the move.  Otherwise we ask the user
Xif she really wants to attack the city.
X*/
X
Xvoid
Xmove_army_to_city (obj, city_loc)
Xpiece_info_t *obj;
Xlong city_loc;
X{
X	piece_info_t *tt;
X
X	tt = find_nfull (TRANSPORT, city_loc);
X
X	if (tt != NULL) move_obj (obj, city_loc);
X
X	else fatal (obj, city_loc,
X	"That's our city, sir!  Do you really want to attack the garrison? ",
X	"Your rebel army was liquidated.");
X}
X
X/*
XCancel automove mode.
X*/
X
Xvoid
Xuser_cancel_auto () {
X	if (!automove)
X		comment ("Not in auto mode!");
X	else {
X		automove = FALSE;
X		comment ("Auto mode cancelled.");
X	}
X}
X
X/*
XRedraw the screen.
X*/
X
Xvoid
Xuser_redraw () {
X	redraw ();
X}
X
X/*
XAwaken an object if it needs to be.  Normally, objects are awakened
Xwhen they are next to an enemy object or an unowned city.  Armies
Xon troop transports are not awakened if they are surrounded by sea.
XWe return true if the object is now awake.  Objects are never
Xcompletely awoken here if their function is a destination.  But we
Xwill return TRUE if we want the user to have control.
X*/
X
Xint
Xawake (obj)
Xpiece_info_t *obj;
X{
X	int i;
X	char c;
X	long t;
X
X	if (obj->type == ARMY && vmap_at_sea (user_map, obj->loc)) {
X	    obj->moved = piece_attr[ARMY].range;
X	    return (FALSE);
X	}
X	if (obj->func == NOFUNC) return (TRUE); /* object is awake */
X	
X	if (obj->type == FIGHTER /* wake fighters */
X	    && obj->func != LAND /* that aren't returning to base */
X	    && obj->func < 0 /* and which don't have a path */
X	    && obj->range <= find_nearest_city (obj->loc, USER, &t) + 2) {
X		obj->func = NOFUNC; /* wake piece */
X		return (TRUE);
X	}
X	for (i = 0; i < 8; i++) { /* for each surrounding cell */
X		c = user_map[obj->loc+dir_offset[i]].contents;
X
X		if (islower (c) || c == '*' || c == 'X') {
X			if (obj->func < 0) obj->func = NOFUNC; /* awaken */
X			return (TRUE);
X		}
X	}
X	return (FALSE);
X}
X
X/*
XQuestion the user about a fatal move.  If the user responds 'y',
Xprint out the response and kill the object.
X*/
X
Xvoid
Xfatal (obj, loc, message, response)
Xpiece_info_t *obj;
Xlong loc;
Xchar *message;
Xchar *response;
X{
X	if (getyn (message)) {
X		comment (response);
X		kill_obj (obj, loc);
X	}
X}
//E*O*F usermove.c//

echo x - game.c
sed -e 's/^X//' > "game.c" << '//E*O*F game.c//'
X/* %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */
X
X/*
X *    Copyright (C) 1987, 1988 Chuck Simmons
X * 
X * See the file COPYING, distributed with empire, for restriction
X * and warranty information.
X */
X
X/*
Xgame.c -- Routines to initialize, save, and restore a game.
X*/
X
X#ifdef SYSV
X#include <string.h>
X#else
X#include <strings.h>
X#endif
X
X#include <ctype.h>
X#include <curses.h>
X#include "empire.h"
X#include "extern.h"
X
X/*
XInitialize a new game.  Here we generate a new random map, put cities
Xon the map, select cities for each opponent, and zero out the lists of
Xpieces on the board.
X*/
X
Xvoid init_game () {
X	void make_map(), place_cities();
X
X	long i;
X
X	kill_display (); /* nothing on screen */
X	automove = FALSE;
X	resigned = FALSE;
X	debug = FALSE;
X	print_debug = FALSE;
X	print_vmap = FALSE;
X	trace_pmap = FALSE;
X	save_movie = FALSE;
X	win = 0;
X	date = 0; /* no date yet */
X	user_score = 0;
X	comp_score = 0;
X	
X	for (i = 0; i < MAP_SIZE; i++) {
X		user_map[i].contents = ' '; /* nothing seen yet */
X		user_map[i].seen = 0;
X		comp_map[i].contents = ' ';
X		comp_map[i].seen = 0;
X	}
X	for (i = 0; i < NUM_OBJECTS; i++) {
X		user_obj[i] = NULL;
X		comp_obj[i] = NULL;
X	}
X	free_list = NULL; /* nothing free yet */
X	for (i = 0; i < LIST_SIZE; i++) { /* for each object */
X		piece_info_t *obj = &(object[i]);
X		obj->hits = 0; /* mark object as dead */
X		obj->owner = UNOWNED;
X		LINK (free_list, obj, piece_link); 
X	}
X
X	make_map (); /* make land and water */
X
X	do {
X		for (i = 0; i < MAP_SIZE; i ++) { /* remove cities */
X			if (map[i].contents == '*')
X				map[i].contents = '+'; /* land */
X		}
X		place_cities (); /* place cities on map */
X	} while (!select_cities ()); /* choose a city for each player */
X}
X
X/*
XCreate a map.  To do this, we first randomly assign heights to each
Xmap location.  Then we smooth these heights.  The more we smooth,
Xthe better the land and water will clump together.  Then we decide
Xhow high the land will be.  We attempt to choose enough land to meet
Xsome required percentage.
X
XThere are two parameters to this algorithm:  the amount we will smooth,
Xand the ratio of land to water.  The user can provide these numbers
Xat program start up.
X*/
X
X#define MAX_HEIGHT 999	/* highest height */
X
X/* these arrays give some compilers problems when they are automatic */
Xstatic int height[2][MAP_SIZE];
Xstatic int height_count[MAX_HEIGHT+1];
X
Xvoid make_map () {
X	int from, to, k;
X	long i, j, sum, loc;
X
X	for (i = 0; i < MAP_SIZE; i++) /* fill map with random sand */
X		height[0][i] = irand (MAX_HEIGHT);
X
X	from = 0;
X	to = 1;
X	for (i = 0; i < SMOOTH; i++) { /* smooth the map */
X	    for (j = 0; j < MAP_SIZE; j++) {
X		sum = height[from][j];
X		for (k = 0; k < 8; k++) {
X			loc = j + dir_offset[k];
X			/* edges get smoothed in a wierd fashion */
X			if (loc < 0 || loc >= MAP_SIZE) loc = j;
X			sum += height[from][loc];
X		}
X		height[to][j] = sum / 9;
X	    }
X	    k = to; /* swap to and from */
X	    to = from;
X	    from = k;
X	}
X
X	/* count the number of cells at each height */
X	for (i = 0; i <= MAX_HEIGHT; i++)
X		height_count[i] = 0;
X
X	for (i = 0; i <= MAP_SIZE; i++)
X		height_count[height[from][i]]++;
X
X	/* find the water line */
X	loc = MAX_HEIGHT; /* default to all water */
X	sum = 0;
X	for (i = 0; i <= MAX_HEIGHT; i++) {
X		sum += height_count[i];
X		if (sum * 100 / MAP_SIZE > WATER_RATIO && sum >= NUM_CITY) {
X			loc = i; /* this is last height that is water */
X			break;
X		}
X	}
X
X	/* mark the land and water */
X	for (i = 0; i < MAP_SIZE; i ++) {
X		if (height[from][i] > loc)
X			map[i].contents = '+'; /* land */
X		else map[i].contents = '.'; /* water */
X
X		map[i].objp = NULL; /* nothing in cell yet */
X		map[i].cityp = NULL;
X
X		j = loc_col (i);
X		k = loc_row (i);
X
X		map[i].on_board = !(j == 0 || j == MAP_WIDTH-1 
X				 || k == 0 || k == MAP_HEIGHT-1);
X	}
X}
X
X/*
XRandomly place cities on the land.  There is a minimum distance that
Xshould exist between cities.  We maintain a list of acceptable land cells
Xon which a city may be placed.  We randomly choose elements from this
Xlist until all the cities are placed.  After each choice of a land cell
Xfor a city, we remove land cells which are too close to the city.
X*/
X
X/* avoid compiler problems with large automatic arrays */
Xstatic long land[MAP_SIZE];
X
Xvoid place_cities () {
X	long regen_land(), remove_land();
X
X	long placed, i, loc;
X	long num_land;
X
X	num_land = 0; /* nothing in land array yet */
X	placed = 0; /* nothing placed yet */
X	while (placed < NUM_CITY) {
X		while (num_land == 0) num_land = regen_land (placed);
X		i = irand (num_land-1); /* select random piece of land */
X		loc = land[i];
X		
X		city[placed].loc = loc;
X		city[placed].owner = UNOWNED;
X		city[placed].work = 0;
X		city[placed].prod = NOPIECE;
X		
X		for (i = 0; i < NUM_OBJECTS; i++)
X			city[placed].func[i] = NOFUNC; /* no function */
X			
X		map[loc].contents = '*';
X		map[loc].cityp = &(city[placed]);
X		placed++;
X
X		/* Now remove any land too close to selected land. */
X		num_land = remove_land (loc, num_land);
X	}
X}
X
X/*
XWhen we run out of available land, we recreate our land list.  We
Xput all land in the list, decrement the min_city_dist, and then
Xremove any land which is too close to a city.
X*/
X
Xlong regen_land (placed)
Xlong placed;
X{
X	long num_land;
X	long i;
X
X	num_land = 0;
X	for (i = 0; i < MAP_SIZE; i++) {
X		if (map[i].on_board && map[i].contents == '+') {
X			land[num_land] = i; /* remember piece of land */
X			num_land++; /* remember number of pieces */
X		}
X	}
X	if (placed > 0) { /* don't decrement 1st time */
X		MIN_CITY_DIST -= 1;
X		ASSERT (MIN_CITY_DIST >= 0);
X	}
X	for (i = 0; i < placed; i++) { /* for each placed city */
X		num_land = remove_land (city[i].loc, num_land);
X	}
X	return (num_land);
X}
X
X/*
XRemove land that is too close to a city.
X*/
X
Xlong remove_land (loc, num_land)
Xlong loc, num_land;
X{
X	long new, i;
X
X	new = 0; /* nothing kept yet */
X	for (i = 0; i < num_land; i++) {
X		if (dist (loc, land[i]) >= MIN_CITY_DIST) {
X			land[new] = land[i];
X			new++;
X		}
X	}
X	return (new);
X}
X
X/*
XHere we select the cities for the user and the computer.  Our choice of
Xcities will be heavily dependent on the difficulty level the user desires.
X
XOur algorithm will not guarantee that either player will eventually be
Xable to move armies to any continent on the map.  There may be continents
Xwhich are unreachable by sea.  Consider the case of an island in a lake.
XIf the lake has no shore cities, then there is no way for a boat to reach
Xthe island.  Our hope is that there will be enough water on the map, or enough
Xland, and that there will be enough cities, to make this case extremely rare.
X
XFirst we make a list of continents which contain at least two cities, one
Xor more of which is on the coast.  If there are no such continents, we return
XFALSE, and our caller should decide again where cities should be placed
Xon the map.  While making this list, we will rank the continents.  Our ranking
Xis based on the thought that shore cities are better than inland cities,
Xthat any city is very important, and that the land area of a continent
Xis mildly important.  Usually, we expect every continent to have a different
Xranking.  It will be unusual to have two continents with the same land area,
Xthe same number of shore cities, and the same number of inland cities.  When
Xthis is not the case, the first city encountered will be given the highest
Xrank.
X
XWe then rank pairs of continents.  We tell the user the number of different
Xranks, and ask the user what rank they want to use.  This is how the
Xuser specifies the difficulty level.  Using that pair, we have now decided
Xon a continent for each player.  We now choose a random city on each continent,
Xmaking sure the cities are not the same.
X*/
X
X#define MAX_CONT 10 /* most continents we will allow */
X
Xtypedef struct cont { /* a continent */
X	long value; /* value of continent */
X	int ncity; /* number of cities */
X	city_info_t * cityp[NUM_CITY]; /* pointer to city */
X} cont_t;
X
Xtypedef struct pair {
X	long value; /* value of pair for user */
X	int user_cont; /* index to user continent */
X	int comp_cont; /* index to computer continent */
X} pair_t;
X
Xstatic int marked[MAP_SIZE]; /* list of examine cells */
Xstatic int ncont; /* number of continents */
Xstatic cont_t cont_tab[MAX_CONT]; /* list of good continenets */
Xstatic int rank_tab[MAX_CONT]; /* indices to cont_tab in order of rank */
Xstatic pair_t pair_tab[MAX_CONT*MAX_CONT]; /* ranked pairs of continents */
X
Xint select_cities () {
X	void find_cont(), make_pair();
X
X	long compi, useri;
X	city_info_t *compp, *userp;
X	int comp_cont, user_cont;
X	int pair;
X
X	find_cont (); /* find and rank the continents */
X	if (ncont == 0) return (FALSE); /* there are no good continents */
X
X	make_pair (); /* create list of ranked pairs */
X
X	(void) sprintf (jnkbuf,
X		"Choose a difficulty level where 0 is easy and %d is hard: ",
X		ncont*ncont-1);
X
X	pair = get_range (jnkbuf, 0, ncont*ncont-1);
X	comp_cont = pair_tab[pair].comp_cont;
X	user_cont = pair_tab[pair].user_cont;
X
X	compi = irand ((long)cont_tab[comp_cont].ncity);
X	compp = cont_tab[comp_cont].cityp[compi];
X
X	do { /* select different user city */
X		useri = irand ((long)cont_tab[user_cont].ncity);
X		userp = cont_tab[user_cont].cityp[useri];
X	} while (userp == compp);
X
X	addprintf ("Your city is at %d.", userp->loc);
X	delay (); /* let user see output before we set_prod */
X
X	/* update city and map */
X	compp->owner = COMP;
X	compp->prod = ARMY;
X	compp->work = 0;
X	scan (comp_map, compp->loc);
X
X	userp->owner = USER;
X	userp->work = 0;
X	scan (user_map, userp->loc);
X	set_prod (userp);
X	return (TRUE);
X}
X
X/*
XFind all continents with 2 cities or more, one of which must be a shore
Xcity.  We rank the continents.
X*/
X
Xvoid find_cont () {
X	long i;
X	long mapi;
X
X	for (i = 0; i < MAP_SIZE; i++) marked[i] = 0; /* nothing marked yet */
X
X	ncont = 0; /* no continents found yet */
X	mapi = 0;
X
X	while (ncont < MAX_CONT)
X		if (!find_next (&mapi)) return; /* all found */
X}
X
X/*
XFind the next continent and insert it in the rank table.
XIf there are no more continents, we return false.
X*/
X
Xint find_next (mapi)
Xlong *mapi;
X{
X	long i, val;
X
X	for (;;) {
X		if (*mapi >= MAP_SIZE) return (FALSE);
X
X		if (!map[*mapi].on_board || marked[*mapi]
X			|| map[*mapi].contents == '.') *mapi += 1;
X		else if (good_cont (*mapi)) {
X			rank_tab[ncont] = ncont; /* insert cont in rank tab */
X			val = cont_tab[ncont].value;
X
X			for (i = ncont; i > 0; i--) { /* bubble up new rank */
X				if (val > cont_tab[rank_tab[i-1]].value) {
X					rank_tab[i] = rank_tab[i-1];
X					rank_tab[i-1] = ncont;
X				}
X				else break;
X			}
X			ncont++; /* count continents */
X			return (TRUE);
X		}
X	}
X}
X
X/*
XMap out the current continent.  We mark every piece of land on the continent,
Xcount the cities, shore cities, and land area of the continent.  If the
Xcontinent contains 2 cities and a shore city, we set the value of the
Xcontinent and return true.  Otherwise we return false.
X*/
X
Xstatic long ncity, nland, nshore;
X
Xint good_cont (mapi)
Xlong mapi;
X{
X	static void mark_cont();
X
X	long val;
X
X	ncity = 0; /* nothing seen yet */
X	nland = 0;
X	nshore = 0;
X
X	mark_cont (mapi);
X
X	if (nshore < 1 || ncity < 2) return (FALSE);
X
X	/* The first two cities, one of which must be a shore city,
X	don't contribute to the value.  Otherwise shore cities are
X	worth 3/2 an inland city.  A city is worth 1000 times as much
X	as land area. */
X
X	if (ncity == nshore) val = (nshore - 2) * 3;
X	else val = (nshore-1) * 3 + (ncity - nshore - 1) * 2;
X
X	val *= 1000; /* cities are worth a lot */
X	val += nland;
X	cont_tab[ncont].value = val;
X	cont_tab[ncont].ncity = ncity;
X	return (TRUE);
X}
X
X/*
XMark a continent.  This recursive algorithm marks the current square
Xand counts it if it is land or city.  If it is city, we also check
Xto see if it is a shore city, and we install it in the list of
Xcities for the continent.  We then examine each surrounding cell.
X*/
X
Xstatic void
Xmark_cont (mapi)
Xlong mapi;
X{
X	int i;
X
X	if (marked[mapi] || map[mapi].contents == '.'
X		|| !map[mapi].on_board) return;
X
X	marked[mapi] = 1; /* mark this cell seen */
X	nland++; /* count land on continent */
X
X	if (map[mapi].contents == '*') { /* a city? */
X		cont_tab[ncont].cityp[ncity] = map[mapi].cityp;
X		ncity++;
X		if (rmap_shore (mapi)) nshore++;
X	}
X
X	for (i = 0; i < 8; i++) /* look at surrounding squares */
X		mark_cont (mapi + dir_offset[i]);
X}
X
X/*
XCreate a list of pairs of continents in a ranked order.  The first
Xelement in the list is the pair which is easiest for the user to
Xwin with.  Our ranking is simply based on the difference in value
Xbetween the user's continent and the computer's continent.
X*/
X
Xvoid make_pair () {
X	int i, j, k, npair;
X	long val;
X
X	npair = 0; /* none yet */
X
X	for (i = 0; i < ncont; i++)
X	for (j = 0; j < ncont; j++) { /* loop through all continents */
X		val = cont_tab[i].value - cont_tab[j].value;
X		pair_tab[npair].value = val;
X		pair_tab[npair].user_cont = i;
X		pair_tab[npair].comp_cont = j;
X
X		for (k = npair; k > 0; k--) { /* bubble up new rank */
X			if (val > pair_tab[k-1].value) {
X				pair_tab[k] = pair_tab[k-1];
X				pair_tab[k-1].user_cont = i;
X				pair_tab[k-1].comp_cont = j;
X			}
X			else break;
X		}
X		npair++; /* count pairs */
X	}
X}
X
X/*
XSave a game.  We save the game in emp_save.dat.  Someday we may want
Xto ask the user for a file name.  If we cannot save the game, we will
Xtell the user why.
X*/
X
X/* macro to save typing; write an array, return if it fails */
X#define wbuf(buf) if (!xwrite (f, (char *)buf, sizeof (buf))) return
X#define wval(val) if (!xwrite (f, (char *)&val, sizeof (val))) return
X
Xvoid save_game () {
X	FILE *f; /* file to save game in */
X
X	f = fopen ("empsave.dat", "w"); /* open for output */
X	if (f == NULL) {
X		perror ("Cannot save empsave.dat");
X		return;
X	}
X	wbuf (map);
X	wbuf (comp_map);
X	wbuf (user_map);
X	wbuf (city);
X	wbuf (object);
X	wbuf (user_obj);
X	wbuf (comp_obj);
X	wval (free_list);
X	wval (date);
X	wval (automove);
X	wval (resigned);
X	wval (debug);
X	wval (win);
X	wval (save_movie);
X	wval (user_score);
X	wval (comp_score);
X
X	(void) fclose (f);
X	topmsg (3, "Game saved.");
X}
X
X/*
XRecover a saved game from emp_save.dat.
XWe return TRUE if we succeed, otherwise FALSE.
X*/
X
X#define rbuf(buf) if (!xread (f, (char *)buf, sizeof(buf))) return (FALSE);
X#define rval(val) if (!xread (f, (char *)&val, sizeof(val))) return (FALSE);
X
Xint restore_game () {
X	void read_embark();
X	
X	FILE *f; /* file to save game in */
X	long i;
X	piece_info_t **list;
X	piece_info_t *obj;
X
X	f = fopen ("empsave.dat", "r"); /* open for input */
X	if (f == NULL) {
X		perror ("Cannot open empsave.dat");
X		return (FALSE);
X	}
X	rbuf (map);
X	rbuf (comp_map);
X	rbuf (user_map);
X	rbuf (city);
X	rbuf (object);
X	rbuf (user_obj);
X	rbuf (comp_obj);
X	rval (free_list);
X	rval (date);
X	rval (automove);
X	rval (resigned);
X	rval (debug);
X	rval (win);
X	rval (save_movie);
X	rval (user_score);
X	rval (comp_score);
X
X	/* Our pointers may not be valid because of source
X	changes or other things.  We recreate them. */
X	
X	free_list = NULL; /* zero all ptrs */
X	for (i = 0; i < MAP_SIZE; i++) {
X		map[i].cityp = NULL;
X		map[i].objp = NULL;
X	}
X	for (i = 0; i < LIST_SIZE; i++) {
X		object[i].loc_link.next = NULL;
X		object[i].loc_link.prev = NULL;
X		object[i].cargo_link.next = NULL;
X		object[i].cargo_link.prev = NULL;
X		object[i].piece_link.next = NULL;
X		object[i].piece_link.prev = NULL;
X		object[i].ship = NULL;
X		object[i].cargo = NULL;
X	}
X	for (i = 0; i < NUM_OBJECTS; i++) {
X		comp_obj[i] = NULL;
X		user_obj[i] = NULL;
X	}
X	/* put cities on map */
X	for (i = 0; i < NUM_CITY; i++)
X		map[city[i].loc].cityp = &(city[i]);
X	
X	/* put pieces in free list or on map and in object lists */
X	for (i = 0; i < LIST_SIZE; i++) {
X		obj = &(object[i]);
X		if (object[i].owner == UNOWNED || object[i].hits == 0) {
X			LINK (free_list, obj, piece_link);
X		}
X		else {
X			list = LIST (object[i].owner);
X			LINK (list[object[i].type], obj, piece_link);
X			LINK (map[object[i].loc].objp, obj, loc_link);
X		}
X	}
X	
X	/* Embark armies and fighters. */
X	read_embark (user_obj[TRANSPORT], ARMY);
X	read_embark (user_obj[CARRIER], FIGHTER);
X	read_embark (comp_obj[TRANSPORT], ARMY);
X	read_embark (comp_obj[CARRIER], FIGHTER);
X	
X	(void) fclose (f);
X	kill_display (); /* what we had is no longer good */
X	topmsg (3, "Game restored from empsave.dat.");
X	return (TRUE);
X}
X	
X/*
XEmbark cargo on a ship.  We loop through the list of ships.
XWe then loop through the pieces at the ship's location until
Xthe ship has the same amount of cargo it previously had.
X*/
X
Xvoid read_embark (list, piece_type)
Xpiece_info_t *list;
Xint piece_type;
X{
X	void inconsistent();
X
X	piece_info_t *ship;
X	piece_info_t *obj;
X	int count;
X
X	for (ship = list; ship != NULL; ship = ship->piece_link.next) {
X		count = ship->count; /* get # of pieces we need */
X		if (count < 0) inconsistent ();
X		ship->count = 0; /* nothing on board yet */
X		for (obj = map[ship->loc].objp; obj && count;
X		    obj = obj->loc_link.next) {
X			if (obj->ship == NULL && obj->type == piece_type) {
X				embark (ship, obj);
X				count -= 1;
X			}
X		}
X		if (count) inconsistent ();
X	}
X}
X
Xvoid inconsistent () {
X	(void) printf ("empsave.dat is inconsistent.  Please remove it.\n");
X	exit (1);
X}
X
X/*
XWrite a buffer to a file.  If we cannot write everything, return FALSE.
XAlso, tell the user why the write did not work if it didn't.
X*/
X
Xint xwrite (f, buf, size)
XFILE *f;
Xchar *buf;
Xint size;
X{
X	int bytes;
X 
X	bytes = fwrite (buf, 1, size, f);
X	if (bytes == -1) {
X		perror ("Write to save file failed");
X		return (FALSE);
X	}
X	if (bytes != size) {
X		perror ("Cannot complete write to save file.\n");
X		return (FALSE);
X	}
X	return (TRUE);
X}
X
X/*
XRead a buffer from a file.  If the read fails, we tell the user why
Xand return FALSE.
X*/
X
Xint xread (f, buf, size)
XFILE *f;
Xchar *buf;
Xint size;
X{
X	int bytes;
X
X	bytes = fread (buf, 1, size, f);
X	if (bytes == -1) {
X		perror ("Read from save file failed");
X		return (FALSE);
X	}
X	if (bytes != size) {
X		perror ("Saved file is too short.\n");
X		return (FALSE);
X	}
X	return (TRUE);
X}
X
X/*
XSave a movie screen.  For each cell on the board, we write out
Xthe character that would appear on either the user's or the
Xcomputer's screen.  This information is appended to 'empmovie.dat'.
X*/
X
Xextern char city_char[];
Xstatic char mapbuf[MAP_SIZE];
X
Xvoid
Xsave_movie_screen ()
X{
X	FILE *f; /* file to save game in */
X	long i;
X	piece_info_t *p;
X
X	f = fopen ("empmovie.dat", "a"); /* open for append */
X	if (f == NULL) {
X		perror ("Cannot open empmovie.dat");
X		return;
X	}
X
X	for (i = 0; i < MAP_SIZE; i++) {
X		if (map[i].cityp) mapbuf[i] = city_char[map[i].cityp->owner];
X		else {
X			p = find_obj_at_loc (i);
X			
X			if (!p) mapbuf[i] = map[i].contents;
X			else if (p->owner == USER)
X				mapbuf[i] = piece_attr[p->type].sname;
X			else mapbuf[i] = tolower (piece_attr[p->type].sname);
X		}
X	}
X	wbuf (mapbuf);
X	(void) fclose (f);
X}
X
X/*
XReplay a movie.  We read each buffer from the file and
Xprint it using a zoomed display.
X*/
X
Xvoid
Xreplay_movie ()
X{
X	void print_movie_cell();
X
X	FILE *f; /* file to save game in */
X	int row_inc, col_inc;
X	int r, c;
X	int round;
X
X	
X	f = fopen ("empmovie.dat", "r"); /* open for input */
X	if (f == NULL) {
X		perror ("Cannot open empmovie.dat");
X		return;
X	}
X	round = 0;
X	clear_screen ();
X	for (;;) {
X		if (fread ((char *)mapbuf, 1, sizeof (mapbuf), f) != sizeof (mapbuf)) break;
X		round += 1;
X		
X		stat_display (mapbuf, round);
X		
X		row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
X		col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
X	
X		for (r = 0; r < MAP_HEIGHT; r += row_inc)
X		for (c = 0; c < MAP_WIDTH; c += col_inc)
X		print_movie_cell (mapbuf, r, c, row_inc, col_inc);
X		
X		(void) refresh ();
X		delay ();
X	}
X	(void) fclose (f);
X}
X
X/*
XDisplay statistics about the game.  At the top of the screen we
Xprint:
X
Xnn O  nn A  nn F  nn P  nn D  nn S  nn T  nn C  nn B  nn Z  xxxxx
Xnn X  nn a  nn f  nn p  nn d  nn s  nn t  nn c  nn b  nn z  xxxxx
X
XThere may be objects in cities and boats that aren't displayed.
XThe "xxxxx" field is the cumulative cost of building the hardware.
X*/
X
X/* in declared order, with city first */
Xstatic char *pieces = "OAFPDSTCBZXafpdstcbz";
X
Xstat_display (mbuf, round)
Xchar *mbuf;
Xint round;
X{
X	long i;
X	int counts[2*NUM_OBJECTS+2];
X	int user_cost, comp_cost;
X	char *p;
X	
X	(void) bzero ((char *)counts, sizeof (counts));
X	
X	for (i = 0; i < MAP_SIZE; i++) {
X		p = strchr (pieces, mbuf[i]);
X		if (p) counts[p-pieces] += 1;
X	}
X	user_cost = 0;
X	for (i = 1; i <= NUM_OBJECTS; i++)
X		user_cost += counts[i] * piece_attr[i-1].build_time;
X		
X	comp_cost = 0;
X	for (i = NUM_OBJECTS+2; i <= 2*NUM_OBJECTS+1; i++)
X		comp_cost += counts[i] * piece_attr[i-NUM_OBJECTS-2].build_time;
X		
X	for (i = 0; i < NUM_OBJECTS+1; i++) {
X		pos_str (1, (int) i * 6, "%2d %c  ", counts[i], pieces[i]);
X		pos_str (2,(int) i * 6, "%2d %c  ", counts[i+NUM_OBJECTS+1], pieces[i+NUM_OBJECTS+1]);
X	}
X
X	pos_str (1, (int) i * 6, "%5d", user_cost);
X	pos_str (2, (int) i * 6, "%5d", comp_cost);
X	pos_str (0, 0, "Round %3d", (round + 1) / 2);
X}
X
X/*
XPrint a single cell in condensed format.
X*/
X
Xextern char zoom_list[];
X
Xvoid
Xprint_movie_cell (mbuf, row, col, row_inc, col_inc)
Xchar *mbuf;
Xint row, col;
Xint row_inc, col_inc;
X{
X	int r, c;
X	char cell;
X
X	cell = ' ';
X	for (r = row; r < row + row_inc; r++)
X	for (c = col; c < col + col_inc; c++)
X	if (strchr (zoom_list, mbuf[row_col_loc(r,c)])
X		< strchr (zoom_list, cell))
X	cell = mbuf[row_col_loc(r,c)];
X	
X	(void) move (row/row_inc + NUMTOPS, col/col_inc);
X	(void) addch ((chtype)cell);
X}
//E*O*F game.c//

echo shar: End of archive 4 \(of 6\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
        MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]*isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0



More information about the Alt.sources mailing list