From: Eric S. Raymond Date: Tue, 25 Jul 2006 16:47:25 +0000 (+0000) Subject: Initial revision X-Git-Tag: 1.8~13 X-Git-Url: http://thyrsus.com/gitweb/?a=commitdiff_plain;h=ab1d5a3ae6b7599b1a1ea33dfd9cb2dfb16b5667;p=vms-empire.git Initial revision --- diff --git a/object.c b/object.c new file mode 100644 index 0000000..e8d3556 --- /dev/null +++ b/object.c @@ -0,0 +1,672 @@ +/* %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */ + +/* + * Copyright (C) 1987, 1988 Chuck Simmons + * + * See the file COPYING, distributed with empire, for restriction + * and warranty information. + */ + +/* +object.c -- routines for manipulating objects. +*/ + +#ifdef SYSV +#include +#else +#include +#endif + +#include +#include +#include "empire.h" +#include "extern.h" + +extern int get_piece_name(); + +/* +Find the nearest city to a location. Return the location +of the city and the estimated cost to reach the city. +Distances are computed as straight-line distances. +*/ + +int +find_nearest_city (loc, owner, city_loc) +long loc; +int owner; +long *city_loc; +{ + long best_dist, best_loc; + long new_dist, i; + + best_dist = INFINITY; + best_loc = loc; + + for (i = 0; i < NUM_CITY; i++) + if (city[i].owner == owner) { + new_dist = dist (loc, city[i].loc); + if (new_dist < best_dist) { + best_dist = new_dist; + best_loc = city[i].loc; + } + } + *city_loc = best_loc; + return best_dist; +} + +/* +Given the location of a city, return the index of that city. +*/ + +city_info_t *find_city (loc) +long loc; +{ + return (map[loc].cityp); +} + +/* +Return the number of moves an object gets to make. This amount +is based on the damage the object has suffered and the number of +moves it normally gets to make. The object always gets to make +at least one move, assuming it is not dead. Damaged objects move +at a fraction of their normal speed. An object which has lost +half of its hits moves at half-speed, for example. +*/ + +int obj_moves (obj) +piece_info_t *obj; +{ + return (piece_attr[obj->type].speed * obj->hits + + piece_attr[obj->type].max_hits - 1) /* round up */ + / piece_attr[obj->type].max_hits; +} + +/* +Figure out the capacity for an object. +*/ + +int obj_capacity (obj) +piece_info_t *obj; +{ + return (piece_attr[obj->type].capacity * obj->hits + + piece_attr[obj->type].max_hits - 1) /* round up */ + / piece_attr[obj->type].max_hits; +} + +/* +Search for an object of a given type at a location. We scan the +list of objects at the given location for one of the given type. +*/ + +piece_info_t *find_obj (type, loc) +int type; +long loc; +{ + piece_info_t *p; + + for (p = map[loc].objp; p != NULL; p = p->loc_link.next) + if (p->type == type) return (p); + + return (NULL); +} + +/* +Find a non-full item of the appropriate type at the given location. +*/ + +piece_info_t *find_nfull (type, loc) +int type; +long loc; +{ + piece_info_t *p; + + for (p = map[loc].objp; p != NULL; p = p->loc_link.next) + if (p->type == type) { + if (obj_capacity (p) > p->count) return (p); + } + return (NULL); +} + +/* +Look around a location for an unfull transport. Return the location +of the transport if there is one. +*/ + +long +find_transport (owner, loc) +int owner; +long loc; +{ + int i; + long new_loc; + piece_info_t *t; + + for (i = 0; i < 8; i++) { /* look around */ + new_loc = loc + dir_offset[i]; + t = find_nfull (TRANSPORT, new_loc); + if (t != NULL && t->owner == owner) return (new_loc); + } + return (loc); /* no tt found */ +} + +/* +Search a list of objects at a location for any kind of object. +We prefer transports and carriers to other objects. +*/ + +piece_info_t * +find_obj_at_loc (loc) +long loc; +{ + piece_info_t *p, *best; + + best = map[loc].objp; + if (best == NULL) return (NULL); /* nothing here */ + + for (p = best->loc_link.next; p != NULL; p = p->loc_link.next) + if (p->type > best->type && p->type != SATELLITE) + best = p; + + return (best); +} + +/* +If an object is on a ship, remove it from that ship. +*/ + +void disembark (obj) +piece_info_t *obj; +{ + if (obj->ship) { + UNLINK (obj->ship->cargo, obj, cargo_link); + obj->ship->count -= 1; + obj->ship = NULL; + } +} + +/* +Move an object onto a ship. +*/ + +void embark (ship, obj) +piece_info_t *ship, *obj; +{ + obj->ship = ship; + LINK (ship->cargo, obj, cargo_link); + ship->count += 1; +} + +/* +Kill an object. We scan around the piece and free it. If there is +anything in the object, it is killed as well. +*/ + +void kill_obj (obj, loc) +piece_info_t *obj; +long loc; +{ + void kill_one(); + + piece_info_t **list; + view_map_t *vmap; + + vmap = MAP(obj->owner); + list = LIST(obj->owner); + + while (obj->cargo != NULL) /* kill contents */ + kill_one (list, obj->cargo); + + kill_one (list, obj); + scan (vmap, loc); /* scan around new location */ +} + +/* kill an object without scanning */ + +void kill_one (list, obj) +piece_info_t **list; +piece_info_t *obj; +{ + UNLINK (list[obj->type], obj, piece_link); /* unlink obj from all lists */ + UNLINK (map[obj->loc].objp, obj, loc_link); + disembark (obj); + + LINK (free_list, obj, piece_link); /* return object to free list */ + obj->hits = 0; /* let all know this object is dead */ + obj->moved = piece_attr[obj->type].speed; /* object has moved */ +} + +/* +Kill a city. We kill off all objects in the city and set its type +to unowned. We scan around the city's location. +*/ + +void kill_city (cityp) +city_info_t *cityp; +{ + view_map_t *vmap; + piece_info_t *p; + piece_info_t *next_p; + piece_info_t **list; + int i; + + /* change ownership of hardware at this location; but not satellites */ + for (p = map[cityp->loc].objp; p; p = next_p) { + next_p = p->loc_link.next; + + if (p->type == ARMY) kill_obj (p, cityp->loc); + else if (p->type != SATELLITE) { + if (p->type == TRANSPORT) { + list = LIST(p->owner); + + while (p->cargo != NULL) /* kill contents */ + kill_one (list, p->cargo); + } + list = LIST (p->owner); + UNLINK (list[p->type], p, piece_link); + p->owner = (p->owner == USER ? COMP : USER); + list = LIST (p->owner); + LINK (list[p->type], p, piece_link); + + p->func = NOFUNC; + } + } + + if (cityp->owner != UNOWNED) { + vmap = MAP(cityp->owner); + cityp->owner = UNOWNED; + cityp->work = 0; + cityp->prod = NOPIECE; + + for (i = 0; i < NUM_OBJECTS; i++) + cityp->func[i] = NOFUNC; + + scan (vmap, cityp->loc); + } +} + +/* +Produce an item for a city. +*/ + +static int sat_dir[4] = {MOVE_NW, MOVE_SW, MOVE_NE, MOVE_SE}; + +void +produce (cityp) +city_info_t *cityp; +{ + piece_info_t **list; + piece_info_t *new; + + list = LIST (cityp->owner); + + cityp->work -= piece_attr[(int)cityp->prod].build_time; + + ASSERT (free_list); /* can we allocate? */ + new = free_list; + UNLINK (free_list, new, piece_link); + LINK (list[(int)cityp->prod], new, piece_link); + LINK (map[cityp->loc].objp, new, loc_link); + new->cargo_link.next = NULL; + new->cargo_link.prev = NULL; + + new->loc = cityp->loc; + new->func = NOFUNC; + new->hits = piece_attr[(int)cityp->prod].max_hits; + new->owner = cityp->owner; + new->type = cityp->prod; + new->moved = 0; + new->cargo = NULL; + new->ship = NULL; + new->count = 0; + new->range = piece_attr[(int)cityp->prod].range; + + if (new->type == SATELLITE) { /* set random move direction */ + new->func = sat_dir[irand (4)]; + } +} + +/* +Move an object to a location. We mark the object moved, we move +the object to the new square, and we scan around the object. +We also do lots of little maintenance like updating the range +of an object, keeping track of the number of pieces on a boat, +etc. +*/ + +void move_obj (obj, new_loc) +piece_info_t *obj; +long new_loc; +{ + view_map_t *vmap; + long old_loc; + piece_info_t *p; + + ASSERT (obj->hits); + vmap = MAP(obj->owner); + + old_loc = obj->loc; /* save original location */ + obj->moved += 1; + obj->loc = new_loc; + obj->range--; + + disembark (obj); /* remove object from any ship */ + + UNLINK (map[old_loc].objp, obj, loc_link); + LINK (map[new_loc].objp, obj, loc_link); + + /* move any objects contained in object */ + for (p = obj->cargo; p != NULL; p = p->cargo_link.next) { + p->loc = new_loc; + UNLINK (map[old_loc].objp, p, loc_link); + LINK (map[new_loc].objp, p, loc_link); + } + + switch (obj->type) { /* board new ship */ + case FIGHTER: + if (map[obj->loc].cityp == NULL) { /* not in a city? */ + p = find_nfull (CARRIER, obj->loc); + if (p != NULL) embark (p, obj); + } + break; + + case ARMY: + p = find_nfull (TRANSPORT, obj->loc); + if (p != NULL) embark (p, obj); + break; + } + + if (obj->type == SATELLITE) + scan_sat (vmap, obj->loc); + scan (vmap, obj->loc); +} + +/* +Move a satellite. It moves according to the preset direction. +Satellites bounce off the edge of the board. + +We start off with some preliminary routines. +*/ + +/* Return next direction for a sattellite to travel. */ + +static long +bounce (loc, dir1, dir2, dir3) +long loc, dir1, dir2, dir3; +{ + int new_loc; + + new_loc = loc + dir_offset[MOVE_DIR (dir1)]; + if (map[new_loc].on_board) return dir1; + + new_loc = loc + dir_offset[MOVE_DIR (dir2)]; + if (map[new_loc].on_board) return dir2; + + return dir3; +} + +/* Move a satellite one square. */ + +static void +move_sat1 (obj) +piece_info_t *obj; +{ + int dir; + long new_loc; + + dir = MOVE_DIR(obj->func); + new_loc = obj->loc + dir_offset[dir]; + + if (!map[new_loc].on_board) { + switch (obj->func) { + case MOVE_NE: + obj->func = bounce (obj->loc, MOVE_NW, MOVE_SE, MOVE_SW); + break; + case MOVE_NW: + obj->func = bounce (obj->loc, MOVE_NE, MOVE_SW, MOVE_SE); + break; + case MOVE_SE: + obj->func = bounce (obj->loc, MOVE_SW, MOVE_NE, MOVE_NW); + break; + case MOVE_SW: + obj->func = bounce (obj->loc, MOVE_SE, MOVE_NW, MOVE_NE); + break; + default: ABORT; + } + dir = MOVE_DIR(obj->func); + new_loc = obj->loc + dir_offset[dir]; + } + move_obj (obj, new_loc); +} + +/* +Now move the satellite all of its squares. +Satellite burns iff it's range reaches zero. +*/ + +void +move_sat (obj) +piece_info_t *obj; +{ + obj->moved = 0; + + while (obj->moved < obj_moves (obj)) { + move_sat1 (obj); + if (obj->range == 0) { + if (obj->owner == USER) + comment ("Satellite at %d crashed and burned.", loc_disp(obj->loc),0,0,0,0,0,0,0); + ksend ("Satellite at %d crashed and burned.", loc_disp(obj->loc),0,0,0,0,0,0,0); + kill_obj (obj, obj->loc); + } + } +} + +/* +Return true if a piece can move to a specified location. +We are passed the object and the location. The location +must be on the board, and the player's view map must have an appropriate +terrain type for the location. Boats may move into port, armies may +move onto transports, and fighters may move onto cities or carriers. +*/ + +int good_loc (obj, loc) +piece_info_t *obj; +long loc; +{ + view_map_t *vmap; + piece_info_t *p; + + if (!map[loc].on_board) return (FALSE); + + vmap = MAP (obj->owner); + + if (strchr (piece_attr[obj->type].terrain, vmap[loc].contents) != NULL) + return (TRUE); + + /* armies can move into unfull transports */ + if (obj->type == ARMY) { + p = find_nfull (TRANSPORT, loc); + return (p != NULL && p->owner == obj->owner); + } + + /* ships and fighters can move into cities */ + if (map[loc].cityp && map[loc].cityp->owner == obj->owner) + return (TRUE); + + /* fighters can move onto unfull carriers */ + if (obj->type == FIGHTER) { + p = find_nfull (CARRIER, loc); + return (p != NULL && p->owner == obj->owner); + } + + return (FALSE); +} + +void describe_obj (obj) +piece_info_t *obj; +{ + char func[STRSIZE]; + char other[STRSIZE]; + + if (obj->func >= 0) (void) sprintf (func, "%ld", loc_disp(obj->func)); + else (void) sprintf (func, func_name[FUNCI(obj->func)]); + + other[0] = 0; + + switch (obj->type) { /* set other information */ + case FIGHTER: + (void) sprintf (other,"; range = %d",obj->range); + break; + + case TRANSPORT: + (void) sprintf (other,"; armies = %d",obj->count); + break; + + case CARRIER: + (void) sprintf (other,"; fighters = %d",obj->count); + break; + } + + prompt2 ("%s at %d: moves = %d; hits = %d; func = %s%s", + piece_attr[obj->type].name, + loc_disp(obj->loc), + obj_moves (obj) - obj->moved, + obj->hits, + func, + other,0,0); +} + +/* +Scan around a location to update a player's view of the world. For each +surrounding cell, we remember the date the cell was examined, and the +contents of the cell. Notice how we carefully update the cell to first +reflect land, water, or city, then army or fighter, then boat, and finally +city owner. This guarantees that the object we want to display will appear +on top. +*/ + +void +scan (vmap, loc) +view_map_t vmap[]; +long loc; +{ + void update(), check(); + + int i; + long xloc; + +#ifdef DEBUG + check (); /* perform a consistency check */ +#endif + ASSERT (map[loc].on_board); /* passed loc must be on board */ + + for (i = 0; i < 8; i++) { /* for each surrounding cell */ + xloc = loc + dir_offset[i]; + update (vmap, xloc); + } + update (vmap, loc); /* update current location as well */ +} + +/* +Scan a portion of the board for a satellite. +*/ + +void +scan_sat (vmap, loc) +view_map_t *vmap; +long loc; +{ + int i; + long xloc; + + ASSERT (map[loc].on_board); + + for (i = 0; i < 8; i++) { /* for each surrounding cell */ + xloc = loc + 2 * dir_offset[i]; + if (xloc >= 0 && xloc < MAP_SIZE && map[xloc].on_board) + scan (vmap, xloc); + } + scan (vmap, loc); +} + +/* +Update a location. We set the date seen, the land type, object +contents starting with armies, then fighters, then boats, and the +city type. +*/ + +char city_char[] = {'*', 'O', 'X'}; + +void +update (vmap, loc) +view_map_t vmap[]; +long loc; +{ + piece_info_t *p; + + vmap[loc].seen = date; + + if (map[loc].cityp) /* is there a city here? */ + vmap[loc].contents = city_char[map[loc].cityp->owner]; + + else { + p = find_obj_at_loc (loc); + + if (p == NULL) /* nothing here? */ + vmap[loc].contents = map[loc].contents; + else if (p->owner == USER) + vmap[loc].contents = piece_attr[p->type].sname; + else vmap[loc].contents = tolower (piece_attr[p->type].sname); + } + if (vmap == comp_map) + display_locx (COMP, comp_map, loc); + else if (vmap == user_map) + display_locx (USER, user_map, loc); +} + +/* +Set the production for a city. We make sure the city is displayed +on the screen, and we ask the user for the new production. We keep +asking until we get a valid answer. +*/ + +void +set_prod (cityp) +city_info_t *cityp; +{ + int i; + + scan (user_map, cityp->loc); + display_loc_u (cityp->loc); + + for (;;) { + prompt ("What do you want the city at %d to produce? ",loc_disp(cityp->loc),0,0,0,0,0,0,0); + + i = get_piece_name (); + + if (i == NOPIECE) + error ("I don't know how to build those.",0,0,0,0,0,0,0,0); + + else { + cityp->prod = i; + city->work = -(piece_attr[i].build_time / 5); + return; + } + } +} + +/* Get the name of a type of object. */ + +int +get_piece_name () +{ + char c; + int i; + + c = get_chx (); /* get the answer */ + + for (i = 0; i < NUM_OBJECTS; i++) + if (piece_attr[i].sname == c) { + return i; + } + return NOPIECE; +}