Reindent the code.
[vms-empire.git] / object.c
1 /*
2  *    Copyright (C) 1987, 1988 Chuck Simmons
3  * 
4  * See the file COPYING, distributed with empire, for restriction
5  * and warranty information.
6  */
7
8 /*
9 object.c -- routines for manipulating objects.
10 */
11
12 #include <string.h>
13 #include <stdio.h>
14 #include <ctype.h>
15 #include "empire.h"
16 #include "extern.h"
17
18 extern int get_piece_name(void);
19
20 /*
21 Find the nearest city to a location.  Return the location
22 of the city and the estimated cost to reach the city.
23 Distances are computed as straight-line distances.
24 */
25
26 int
27 find_nearest_city(loc_t loc, int owner, loc_t *city_loc)
28 {
29     loc_t best_loc;
30     long best_dist;
31     long new_dist, i;
32         
33     best_dist = INFINITY;
34     best_loc = loc;
35         
36     for (i = 0; i < NUM_CITY; i++) 
37         if (city[i].owner == owner) {
38             new_dist = dist (loc, city[i].loc);
39             if (new_dist < best_dist) {
40                 best_dist = new_dist;
41                 best_loc = city[i].loc;
42             }
43         }
44     *city_loc = best_loc;
45     return best_dist;
46 }
47
48 /*
49 Given the location of a city, return the index of that city.
50 */
51
52 city_info_t *find_city(loc_t loc)
53 {
54     return (map[loc].cityp);
55 }
56
57 /*
58 Return the number of moves an object gets to make.  This amount
59 is based on the damage the object has suffered and the number of
60 moves it normally gets to make.  The object always gets to make
61 at least one move, assuming it is not dead.  Damaged objects move
62 at a fraction of their normal speed.  An object which has lost
63 half of its hits moves at half-speed, for example.
64 */
65
66 int obj_moves (piece_info_t *obj)
67 {
68     return (piece_attr[obj->type].speed * obj->hits
69             + piece_attr[obj->type].max_hits - 1) /* round up */
70         / piece_attr[obj->type].max_hits;
71 }
72
73 /*
74 Figure out the capacity for an object.
75 */
76
77 int obj_capacity(piece_info_t *obj)
78 {
79     return (piece_attr[obj->type].capacity * obj->hits
80             + piece_attr[obj->type].max_hits - 1) /* round up */
81         / piece_attr[obj->type].max_hits;
82 }
83
84 /*
85 Search for an object of a given type at a location.  We scan the
86 list of objects at the given location for one of the given type.
87 */
88
89 piece_info_t *find_obj(int type, loc_t loc)
90 {
91     piece_info_t *p;
92
93     for (p = map[loc].objp; p != NULL; p = p->loc_link.next)
94         if (p->type == type) return (p);
95
96     return (NULL);
97 }
98
99 /*
100 Find a non-full item of the appropriate type at the given location.
101 */
102
103 piece_info_t *find_nfull(int type, loc_t loc)
104 {
105     piece_info_t *p;
106
107     for (p = map[loc].objp; p != NULL; p = p->loc_link.next)
108         if (p->type == type) {
109             if (obj_capacity (p) > p->count) return (p);
110         }
111     return (NULL);
112 }
113
114 /*
115 Look around a location for an unfull transport.  Return the location
116 of the transport if there is one.
117 */
118
119 loc_t
120 find_transport(int owner, loc_t loc)
121 {
122     int i;
123     loc_t new_loc;
124     piece_info_t *t;
125
126     for (i = 0; i < 8; i++) { /* look around */
127         new_loc = loc + dir_offset[i];
128         t = find_nfull (TRANSPORT, new_loc);
129         if (t != NULL && t->owner == owner) return (new_loc);
130     }
131     return (loc); /* no tt found */
132 }
133
134 /*
135 Search a list of objects at a location for any kind of object.
136 We prefer transports and carriers to other objects.
137 */
138
139 piece_info_t *
140 find_obj_at_loc(loc_t loc)
141 {
142     piece_info_t *p, *best;
143         
144     best = map[loc].objp;
145     if (best == NULL) return (NULL); /* nothing here */
146
147     for (p = best->loc_link.next; p != NULL; p = p->loc_link.next)
148         if (p->type > best->type && p->type != SATELLITE)
149             best = p;
150
151     return (best);
152 }
153
154 /*
155 If an object is on a ship, remove it from that ship.
156 */
157
158 void disembark(piece_info_t *obj)
159 {
160     if (obj->ship) {
161         UNLINK (obj->ship->cargo, obj, cargo_link);
162         obj->ship->count -= 1;
163         obj->ship = NULL;
164     }
165 }
166
167 /*
168 Move an object onto a ship.
169 */
170
171 void embark(piece_info_t *ship, piece_info_t *obj)
172 {
173         obj->ship = ship;
174         LINK (ship->cargo, obj, cargo_link);
175         ship->count += 1;
176 }
177
178 /*
179 Kill an object.  We scan around the piece and free it.  If there is
180 anything in the object, it is killed as well.
181 */
182
183 void kill_obj(piece_info_t *obj, loc_t loc)
184 {
185     void kill_one();
186
187     piece_info_t **list;
188     view_map_t *vmap;
189         
190     vmap = MAP(obj->owner);
191     list = LIST(obj->owner);
192         
193     while (obj->cargo != NULL) /* kill contents */
194         kill_one (list, obj->cargo);
195
196     kill_one (list, obj);
197     scan (vmap, loc); /* scan around new location */
198 }
199
200 /* kill an object without scanning */
201
202 void kill_one(piece_info_t **list, piece_info_t *obj)
203 {
204     UNLINK (list[obj->type], obj, piece_link); /* unlink obj from all lists */
205     UNLINK (map[obj->loc].objp, obj, loc_link);
206     disembark (obj);
207
208     LINK (free_list, obj, piece_link); /* return object to free list */
209     obj->hits = 0; /* let all know this object is dead */
210     obj->moved = piece_attr[obj->type].speed; /* object has moved */
211 }
212
213 /*
214 Kill a city.  We kill off all objects in the city and set its type
215 to unowned.  We scan around the city's location.
216 */
217
218 void kill_city(city_info_t *cityp)
219 {
220     view_map_t *vmap;
221     piece_info_t *p;
222     piece_info_t *next_p;
223     piece_info_t **list;
224     int i;
225         
226     /* change ownership of hardware at this location; but not satellites */
227     for (p = map[cityp->loc].objp; p; p = next_p) {
228         next_p = p->loc_link.next;
229                 
230         if (p->type == ARMY) kill_obj (p, cityp->loc);
231         else if (p->type != SATELLITE) {
232             if (p->type == TRANSPORT) {
233                 list = LIST(p->owner);
234                                 
235                 while (p->cargo != NULL) /* kill contents */
236                     kill_one (list, p->cargo);
237             }
238             list = LIST (p->owner);
239             UNLINK (list[p->type], p, piece_link);
240             p->owner = (p->owner == USER ? COMP : USER);
241             list = LIST (p->owner);
242             LINK (list[p->type], p, piece_link);
243                         
244             p->func = NOFUNC;
245         }
246     }
247
248     if (cityp->owner != UNOWNED) {
249         vmap = MAP(cityp->owner);
250         cityp->owner = UNOWNED;
251         cityp->work = 0;
252         cityp->prod = NOPIECE;
253                 
254         for (i = 0; i < NUM_OBJECTS; i++)
255             cityp->func[i] = NOFUNC;
256                 
257         scan (vmap, cityp->loc);
258     }
259 }
260
261 /*
262 Produce an item for a city.
263 */
264
265 static int sat_dir[4] = {MOVE_NW, MOVE_SW, MOVE_NE, MOVE_SE};
266
267 void
268 produce(city_info_t *cityp)
269 {
270     piece_info_t **list;
271     piece_info_t *new;
272         
273     list = LIST (cityp->owner);
274
275     cityp->work -= piece_attr[(int)cityp->prod].build_time;
276         
277     ASSERT (free_list); /* can we allocate? */
278     new = free_list;
279     UNLINK (free_list, new, piece_link);
280     LINK (list[(int)cityp->prod], new, piece_link);
281     LINK (map[cityp->loc].objp, new, loc_link);
282     new->cargo_link.next = NULL;
283     new->cargo_link.prev = NULL;
284         
285     new->loc = cityp->loc;
286     new->func = NOFUNC;
287     new->hits = piece_attr[(int)cityp->prod].max_hits;
288     new->owner = cityp->owner;
289     new->type = cityp->prod;
290     new->moved = 0;
291     new->cargo = NULL;
292     new->ship = NULL;
293     new->count = 0;
294     new->range = piece_attr[(int)cityp->prod].range;
295         
296     if (new->type == SATELLITE) { /* set random move direction */
297         new->func = sat_dir[irand (4)];
298     }
299 }
300
301 /*
302 Move an object to a location.  We mark the object moved, we move
303 the object to the new square, and we scan around the object.
304 We also do lots of little maintenance like updating the range
305 of an object, keeping track of the number of pieces on a boat, 
306 etc.
307 */
308
309 void move_obj(piece_info_t *obj, loc_t new_loc)
310 {
311     view_map_t *vmap;
312     loc_t old_loc;
313     piece_info_t *p;
314
315     ASSERT (obj->hits);
316     vmap = MAP(obj->owner);
317
318     old_loc = obj->loc; /* save original location */
319     obj->moved += 1;
320     obj->loc = new_loc;
321     obj->range--;
322         
323     disembark (obj); /* remove object from any ship */
324         
325     UNLINK (map[old_loc].objp, obj, loc_link);
326     LINK (map[new_loc].objp, obj, loc_link);
327
328     /* move any objects contained in object */
329     for (p = obj->cargo; p != NULL; p = p->cargo_link.next) {
330         p->loc = new_loc;
331         UNLINK (map[old_loc].objp, p, loc_link);
332         LINK (map[new_loc].objp, p, loc_link);
333     }
334         
335     switch (obj->type) { /* board new ship */
336     case FIGHTER:
337         if (map[obj->loc].cityp == NULL) { /* not in a city? */
338             p = find_nfull (CARRIER, obj->loc);
339             if (p != NULL) embark (p, obj);
340         }
341         break;
342
343     case ARMY:
344         p = find_nfull (TRANSPORT, obj->loc);
345         if (p != NULL) embark (p, obj);
346         break;
347     }
348
349     if (obj->type == SATELLITE)
350         scan_sat (vmap, obj->loc);
351     scan (vmap, obj->loc);
352 }
353
354 /*
355 Move a satellite.  It moves according to the preset direction.
356 Satellites bounce off the edge of the board.
357
358 We start off with some preliminary routines.
359 */
360
361 /* Return next direction for a sattellite to travel. */
362
363 static loc_t
364 bounce(loc_t loc, loc_t dir1, loc_t dir2, loc_t dir3)
365 {
366     int new_loc;
367
368     new_loc = loc + dir_offset[MOVE_DIR (dir1)];
369     if (map[new_loc].on_board) return dir1;
370
371     new_loc = loc + dir_offset[MOVE_DIR (dir2)];
372     if (map[new_loc].on_board) return dir2;
373
374     return dir3;
375 }
376
377 /* Move a satellite one square. */
378
379 static void
380 move_sat1(piece_info_t *obj)
381 {
382     int dir;
383     loc_t new_loc;
384
385     dir = MOVE_DIR(obj->func);
386     new_loc = obj->loc + dir_offset[dir];
387
388     if (!map[new_loc].on_board) {
389         switch (obj->func) {
390         case MOVE_NE:
391             obj->func = bounce (obj->loc, MOVE_NW, MOVE_SE, MOVE_SW);
392             break;
393         case MOVE_NW:
394             obj->func = bounce (obj->loc, MOVE_NE, MOVE_SW, MOVE_SE);
395             break;
396         case MOVE_SE:
397             obj->func = bounce (obj->loc, MOVE_SW, MOVE_NE, MOVE_NW);
398             break;
399         case MOVE_SW:
400             obj->func = bounce (obj->loc, MOVE_SE, MOVE_NW, MOVE_NE);
401             break;
402         default: ABORT;
403         }
404         dir = MOVE_DIR(obj->func);
405         new_loc = obj->loc + dir_offset[dir];
406     }
407     move_obj (obj, new_loc);
408 }
409
410 /*
411 Now move the satellite all of its squares.
412 Satellite burns iff it's range reaches zero.
413 */
414                 
415 void
416 move_sat(piece_info_t *obj)
417 {
418     obj->moved = 0;
419         
420     while (obj->moved < obj_moves (obj)) {
421         move_sat1 (obj);
422         if (obj->range == 0) {
423             if (obj->owner == USER)
424                 comment ("Satellite at %d crashed and burned.", loc_disp(obj->loc));
425             ksend ("Satellite at %d crashed and burned.", loc_disp(obj->loc));
426             kill_obj (obj, obj->loc);
427         }
428     }
429 }
430
431 /*
432 Return true if a piece can move to a specified location.
433 We are passed the object and the location.  The location
434 must be on the board, and the player's view map must have an appropriate
435 terrain type for the location.  Boats may move into port, armies may
436 move onto transports, and fighters may move onto cities or carriers.
437 */
438
439 bool good_loc(piece_info_t *obj, loc_t loc)
440 {
441     view_map_t *vmap;
442     piece_info_t *p;
443         
444     if (!map[loc].on_board) return (false);
445
446     vmap = MAP (obj->owner);
447
448     if (strchr (piece_attr[obj->type].terrain, vmap[loc].contents) != NULL)
449         return (true);
450
451     /* armies can move into unfull transports */
452     if (obj->type == ARMY) {
453         p = find_nfull (TRANSPORT, loc);
454         return (p != NULL && p->owner == obj->owner);
455     }
456
457     /* ships and fighters can move into cities */
458     if (map[loc].cityp && map[loc].cityp->owner == obj->owner)
459         return (true);
460
461     /* fighters can move onto unfull carriers */
462     if (obj->type == FIGHTER) {
463         p = find_nfull (CARRIER, loc);
464         return (p != NULL && p->owner == obj->owner);
465     }
466
467     return (false);
468 }
469
470 void describe_obj(piece_info_t *obj)
471 {
472     char func[STRSIZE];
473     char other[STRSIZE];
474
475     if (obj->func >= 0) (void) sprintf (func, "%d", loc_disp(obj->func));
476     else (void) sprintf (func, func_name[FUNCI(obj->func)]);
477         
478     other[0] = 0;
479
480     switch (obj->type) { /* set other information */
481     case FIGHTER:
482         (void) sprintf (other,"; range = %d",obj->range);
483         break;
484
485     case TRANSPORT:
486         (void) sprintf (other,"; armies = %d",obj->count);
487         break;
488
489     case CARRIER:
490         (void) sprintf (other,"; fighters = %d",obj->count);
491         break;
492     }
493
494     prompt ("%s at %d:  moves = %d; hits = %d; func = %s%s",
495             piece_attr[obj->type].name,
496             loc_disp(obj->loc),
497             obj_moves (obj) - obj->moved,
498             obj->hits,
499             func,
500             other);
501 }
502
503 /*
504 Scan around a location to update a player's view of the world.  For each
505 surrounding cell, we remember the date the cell was examined, and the
506 contents of the cell.  Notice how we carefully update the cell to first
507 reflect land, water, or city, then army or fighter, then boat, and finally
508 city owner.  This guarantees that the object we want to display will appear
509 on top.
510 */
511
512 void
513 scan(view_map_t vmap[], loc_t loc)
514 {
515     void update(), check(void);
516
517     int i;
518     loc_t xloc;
519
520 #ifdef DEBUG
521     check (); /* perform a consistency check */
522 #endif
523     ASSERT (map[loc].on_board); /* passed loc must be on board */
524
525     for (i = 0; i < 8; i++) { /* for each surrounding cell */
526         xloc = loc + dir_offset[i];
527         update (vmap, xloc);
528     }
529     update (vmap, loc); /* update current location as well */
530 }
531
532 /*
533 Scan a portion of the board for a satellite.
534 */
535
536 void
537 scan_sat(view_map_t vmap[], loc_t loc)
538 {
539         int i;
540         loc_t xloc;
541         
542         ASSERT (map[loc].on_board);
543
544         for (i = 0; i < 8; i++) { /* for each surrounding cell */
545                 xloc = loc + 2 * dir_offset[i];
546                 if (xloc >= 0 && xloc < MAP_SIZE && map[xloc].on_board)
547                         scan (vmap, xloc);
548         }
549         scan (vmap, loc);
550 }
551
552 /*
553 Update a location.  We set the date seen, the land type, object
554 contents starting with armies, then fighters, then boats, and the
555 city type.
556 */
557
558 char city_char[] = {'*', 'O', 'X'};
559
560 void
561 update(view_map_t vmap[], loc_t loc)
562 {
563     piece_info_t *p;
564
565     vmap[loc].seen = date;
566         
567     if (map[loc].cityp) /* is there a city here? */
568         vmap[loc].contents = city_char[map[loc].cityp->owner];
569         
570     else {
571         p = find_obj_at_loc (loc);
572                 
573         if (p == NULL) /* nothing here? */
574             vmap[loc].contents = map[loc].contents;
575         else if (p->owner == USER)
576             vmap[loc].contents = piece_attr[p->type].sname;
577         else
578             vmap[loc].contents = tolower (piece_attr[p->type].sname);
579     }
580     if (vmap == comp_map)
581         display_locx (COMP, comp_map, loc);
582     else if (vmap == user_map)
583         display_locx (USER, user_map, loc);
584 }
585
586 /*
587 Set the production for a city.  We make sure the city is displayed
588 on the screen, and we ask the user for the new production.  We keep
589 asking until we get a valid answer.
590 */
591
592 void
593 set_prod(city_info_t *cityp)
594 {
595     int i;
596
597     scan (user_map, cityp->loc);
598     display_loc_u (cityp->loc);
599
600     for (;;) {
601         prompt ("What do you want the city at %d to produce? ",loc_disp(cityp->loc));
602
603         i = get_piece_name ();
604                 
605         if (i == NOPIECE)
606             error ("I don't know how to build those.");
607                         
608         else {
609             cityp->prod = i;
610             city->work = -(piece_attr[i].build_time / 5);
611             return;
612         }
613     }
614 }
615
616 /* Get the name of a type of object. */
617
618 int
619 get_piece_name(void)
620 {
621     char c;
622     int i;
623         
624     c = get_chx (); /* get the answer */
625
626     for (i = 0; i < NUM_OBJECTS; i++)
627         if (piece_attr[i].sname == c) {
628             return i;
629         }
630     return NOPIECE;
631 }
632
633 /* end */