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