Documentation and packaging tweaks.
[vms-empire.git] / usermove.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 usermove.c -- Let the user move her troops.
12 */
13
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <string.h>
17 #include "empire.h"
18 #include "extern.h"
19
20 void fatal(piece_info_t *obj,long loc,char *message,char *response);
21 void move_to_dest(piece_info_t *obj,long dest);
22 void move_army_to_city(piece_info_t *obj,long city_loc);
23 int awake(piece_info_t *obj);
24 extern int get_piece_name(void);
25
26 void
27 user_move (void) {
28         void piece_move();
29
30         int i, j, sec, sec_start;
31         piece_info_t *obj, *next_obj;
32         int prod;
33
34         /* First we loop through objects to update the user's view
35         of the world and perform any other necessary processing.
36         We would like to have the world view up to date before
37         asking the user any questions.  This means that we should
38         also scan through all cities before possibly asking the
39         user what to produce in each city. */
40
41         for (i = 0; i < NUM_OBJECTS; i++)
42         for (obj = user_obj[i]; obj != NULL; obj = obj->piece_link.next) {
43                 obj->moved = 0; /* nothing moved yet */
44                 scan (user_map, obj->loc); /* refresh user's view of world */
45         }
46
47         /* produce new hardware */
48         for (i = 0; i < NUM_CITY; i++)
49             if (city[i].owner == USER) {
50                 scan (user_map, city[i].loc);
51                 prod = city[i].prod;
52
53                 if (prod == NOPIECE) { /* need production? */
54                         set_prod (&(city[i])); /* ask user what to produce */
55                 }
56                 else if (city[i].work++ >= (long)piece_attr[prod].build_time) {
57                                 /* kermyt begin */
58                         ksend1("%s has been completed at city %d.\n", piece_attr[prod].article,loc_disp(city[i].loc),0,0,0,0,0,0);
59                                 /* kermyt end */
60                         comment1 ("%s has been completed at city %d.\n", piece_attr[prod].article,loc_disp(city[i].loc),0,0,0,0,0,0);
61
62                         produce (&city[i]);
63                         /* produce should set object.moved to 0 */
64                 }
65         }
66
67         /* move all satellites */
68         for (obj = user_obj[SATELLITE]; obj != NULL; obj = next_obj) {
69                 next_obj = obj->piece_link.next;
70                 move_sat (obj);
71         }
72         
73         sec_start = cur_sector (); /* get currently displayed sector */
74         if (sec_start == -1) sec_start = 0;
75
76         /* loop through sectors, moving every piece in the sector */
77         for (i = sec_start; i < sec_start + NUM_SECTORS; i++) {
78                 sec = i % NUM_SECTORS;
79                 sector_change (); /* allow screen to be redrawn */
80
81                 for (j = 0; j < NUM_OBJECTS; j++) /* loop through obj lists */
82                 for (obj = user_obj[move_order[j]]; obj != NULL;
83                         obj = next_obj) { /* loop through objs in list */
84                         next_obj = obj->piece_link.next;
85
86                         if (!obj->moved) /* object not moved yet? */
87                         if (loc_sector (obj->loc) == sec) /* object in sector? */
88                         piece_move (obj); /* yup; move the object */
89                 }
90                 if (cur_sector () == sec) { /* is sector displayed? */
91                         print_sector_u (sec); /* make screen up-to-date */
92                         (void) refresh (); /* show it to the user */
93                 }
94         }
95         if (save_movie) save_movie_screen ();
96 }
97
98 /*
99 Move a piece.  We loop until all the moves of a piece are made.  Within
100 the loop, we first awaken the piece if it is adjacent to an enemy piece.
101 Then we attempt to handle any preprogrammed function for the piece.  If
102 the piece has not moved after this, we ask the user what to do.
103 */
104
105 void
106 piece_move (obj)
107 piece_info_t *obj;
108 {
109         void move_random(), move_fill(), move_land(), move_explore();
110         void move_path(), move_dir(), move_armyload(), ask_user();
111         void move_armyattack(), move_ttload(), move_repair();
112         void move_transport();
113
114         int changed_loc;
115         int speed, max_hits;
116         int saved_moves;
117         int need_input;
118         long saved_loc;
119         city_info_t *cityp;
120
121         /* set func for piece if on city */
122         cityp = find_city (obj->loc);
123         if (cityp != NULL)
124                 if (cityp->func[obj->type] != NOFUNC)
125                         obj->func = cityp->func[obj->type];
126
127         changed_loc = FALSE; /* not changed yet */
128         speed = piece_attr[obj->type].speed;
129         max_hits = piece_attr[obj->type].max_hits;
130         need_input = FALSE; /* don't require user input yet */
131
132         while (obj->moved < obj_moves (obj)) {
133                 saved_moves = obj->moved; /* save moves made */
134                 saved_loc = obj->loc; /* remember starting location */
135
136                 if (awake (obj) || need_input){ /* need user input? */
137                         ask_user (obj);
138                         topini (); /* clear info lines */
139                         display_loc_u (obj->loc); /* let user see result */
140                         (void) refresh ();
141                         need_input = FALSE; /* we got it */
142                 }
143                 
144                 if (obj->moved == saved_moves) /* user set function? */
145                 switch (obj->func) { /* handle preprogrammed function */
146                 case NOFUNC:    break;
147                 case RANDOM:    move_random (obj); break;
148                 case SENTRY:    obj->moved = speed; break;
149                 case FILL:      move_fill (obj); break;
150                 case LAND:      move_land (obj); break;
151                 case EXPLORE:   move_explore (obj); break;
152                 case ARMYLOAD:  move_armyload (obj); break;
153                 case ARMYATTACK:move_armyattack (obj); break;
154                 case TTLOAD:    move_ttload (obj); break;
155                 case REPAIR:    move_repair (obj); break;
156                 case WFTRANSPORT: move_transport (obj); break;
157
158                 case MOVE_N:
159                 case MOVE_NE:
160                 case MOVE_E:
161                 case MOVE_SE:
162                 case MOVE_S:
163                 case MOVE_SW:
164                 case MOVE_W:
165                 case MOVE_NW:
166                         move_dir (obj); break;
167
168                 default: move_path (obj); break;
169                 }
170
171                 if (obj->moved == saved_moves) need_input = TRUE;
172                 
173                 /* handle fighters specially.  If in a city or carrier, turn
174                 is over and reset range to max.  Otherwise, if
175                 range = 0, fighter crashes and burns and turn is over. */
176
177                 if (obj->type == FIGHTER && obj->hits > 0) {
178                         if ((user_map[obj->loc].contents == 'O'
179                           || user_map[obj->loc].contents == 'C')
180                         && obj->moved > 0) {
181                                 obj->range = piece_attr[FIGHTER].range;
182                                 obj->moved = speed;
183                                 obj->func = NOFUNC;
184                                 comment ("Landing confirmed.",0,0,0,0,0,0,0,0);
185                         }
186                         else if (obj->range == 0) {
187                                 comment ("Fighter at %d crashed and burned.",loc_disp(obj->loc),0,0,0,0,0,0,0);
188                                 kill_obj (obj, obj->loc);
189                         }
190                 }
191
192                 if (saved_loc != obj->loc) changed_loc = TRUE;
193         }
194         /* if a boat is in port, damaged, and never moved, fix some damage */
195         if (obj->hits > 0 /* still alive? */
196                 && !changed_loc /* object never changed location? */
197                 && obj->type != ARMY && obj->type != FIGHTER /* it is a boat? */
198                 && obj->hits < max_hits /* it is damaged? */
199                 && user_map[obj->loc].contents == 'O') /* it is in port? */
200         obj->hits++; /* fix some damage */
201 }
202
203 /*
204 Move a piece at random.  We create a list of empty squares to which
205 the piece can move.  If there are none, we do nothing, otherwise we 
206 move the piece to a random adjacent square.
207 */
208
209 void move_random (obj)
210 piece_info_t *obj;
211 {
212         long loc_list[8];
213         int i, nloc;
214         long loc;
215
216         nloc = 0;
217
218         for (i = 0; i < 8; i++) {
219                 loc = obj->loc + dir_offset[i];
220                 if (good_loc (obj, loc)) {
221                         loc_list[nloc] = loc; /* remember this location */
222                         nloc++; /* count locations we can move to */
223                 }
224         }
225         if (nloc == 0) return; /* no legal move */
226         i = irand ((long)nloc-1); /* choose random direction */
227         move_obj (obj, loc_list[i]); /* move the piece */
228 }
229
230 /*
231 Have a piece explore.  We look for the nearest unexplored territory
232 which the piece can reach and have to piece move toward the
233 territory.
234 */
235
236 void move_explore (obj)
237 piece_info_t *obj;
238 {
239         path_map_t path_map[MAP_SIZE];
240         long loc;
241         char *terrain;
242
243         switch (obj->type) {
244         case ARMY:
245                 loc = vmap_find_lobj (path_map, user_map, obj->loc, &user_army);
246                 terrain = "+";
247                 break;
248         case FIGHTER:
249                 loc = vmap_find_aobj (path_map, user_map, obj->loc, &user_fighter);
250                 terrain = "+.O";
251                 break;
252         default:
253                 loc = vmap_find_wobj (path_map, user_map, obj->loc, &user_ship);
254                 terrain = ".O";
255                 break;
256         }
257         
258         if (loc == obj->loc) return; /* nothing to explore */
259
260         if (user_map[loc].contents == ' ' && path_map[loc].cost == 2)
261                 vmap_mark_adjacent (path_map, obj->loc);
262         else vmap_mark_path (path_map, user_map, loc);
263
264         loc = vmap_find_dir (path_map, user_map, obj->loc, terrain, " ");
265         if (loc != obj->loc) move_obj (obj, loc);
266 }
267
268 /*
269 Move an army onto a transport when it arrives.  We scan around the
270 army to find a non-full transport.  If one is present, we move the
271 army to the transport and waken the army.
272 */
273
274 void
275 move_transport (obj)
276 piece_info_t *obj;
277 {
278         long loc;
279
280         /* look for an adjacent transport */
281         loc = find_transport (USER, obj->loc);
282         
283         if (loc != obj->loc) {
284                 move_obj (obj, loc);
285                 obj->func = NOFUNC;
286         }
287         else obj->moved = piece_attr[obj->type].speed;
288 }
289
290 /*
291 Move an army toward the nearest loading transport.
292 If there is an adjacent transport, move the army onto
293 the transport, and awaken the army.
294 */
295
296 static view_map_t amap[MAP_SIZE];
297
298 void
299 move_armyload (obj)
300 piece_info_t *obj;
301 {
302         long loc;
303         piece_info_t *p;
304         int i;
305
306         ABORT;
307         
308         /* look for an adjacent transport */
309         loc = find_transport (USER, obj->loc);
310
311         if (loc != obj->loc) {
312                 move_obj (obj, loc);
313                 obj->func = NOFUNC;
314         }
315         else { /* look for nearest non-full transport */
316                 (void) memcpy (amap, user_map, sizeof (view_map_t) * MAP_SIZE);
317
318                 /* mark loading transports or cities building transports */
319                 for (p = user_obj[TRANSPORT]; p; p = p->piece_link.next)
320                 if (p->count < obj_capacity (p)) /* not full? */
321                 amap[p->loc].contents = '$';
322                 
323                 for (i = 0; i < NUM_CITY; i++)
324                 if (city[i].owner == USER && city[i].prod == TRANSPORT)
325                 amap[city[i].loc].contents = '$';
326         }
327 }
328                 
329 /*
330 Move an army toward an attackable city or enemy army.
331 */
332
333 void
334 move_armyattack (obj)
335 piece_info_t *obj;
336 {
337         path_map_t path_map[MAP_SIZE];
338         long loc;
339
340         ASSERT (obj->type == ARMY);
341
342         loc = vmap_find_lobj (path_map, user_map, obj->loc, &user_army_attack);
343         
344         if (loc == obj->loc) return; /* nothing to attack */
345
346         vmap_mark_path (path_map, user_map, loc);
347
348         loc = vmap_find_dir (path_map, user_map, obj->loc, "+", "X*a");
349         if (loc != obj->loc) move_obj (obj, loc);
350 }
351
352 void
353 move_ttload (obj)
354 piece_info_t *obj;
355 {
356         ABORT;
357         obj = obj;
358 }
359
360 /*
361 Move a ship toward port.  If the ship is healthy, wake it up.
362 */
363
364 void
365 move_repair (obj)
366 piece_info_t *obj;
367 {
368         path_map_t path_map[MAP_SIZE];
369         long loc;
370
371         ASSERT (obj->type > FIGHTER);
372         
373         if (obj->hits == piece_attr[obj->type].max_hits) {
374                 obj->func = NOFUNC;
375                 return;
376         }
377         
378         if (user_map[obj->loc].contents == 'O') { /* it is in port? */
379                 obj->moved += 1;
380                 return;
381         }
382
383         loc = vmap_find_wobj (path_map, user_map, obj->loc, &user_ship_repair);
384         
385         if (loc == obj->loc) return; /* no reachable city */
386
387         vmap_mark_path (path_map, user_map, loc);
388
389         /* try to be next to ocean to avoid enemy pieces */
390         loc = vmap_find_dir (path_map, user_map, obj->loc, ".O", ".");
391         if (loc != obj->loc) move_obj (obj, loc);
392 }
393
394 /*
395 Here we have a transport or carrier waiting to be filled.  If the
396 object is not full, we set the move count to its maximum value.
397 Otherwise we awaken the object.
398 */
399
400 void move_fill (obj)
401 piece_info_t *obj;
402 {
403         if (obj->count == obj_capacity (obj)) /* full? */
404                 obj->func = NOFUNC; /* awaken full boat */
405         else obj->moved = piece_attr[obj->type].speed;
406 }
407
408 /*
409 Here we have a piece that wants to land at the nearest carrier or
410 owned city.  We scan through the lists of cities and carriers looking
411 for the closest one.  We then move toward that item's location.
412 The nearest landing field must be within the object's range.
413 */
414
415 void
416 move_land (obj)
417 piece_info_t *obj;
418 {
419         long best_dist, best_loc;
420         long new_dist;
421         piece_info_t *p;
422
423         best_dist = find_nearest_city (obj->loc, USER, &best_loc);
424
425         for (p = user_obj[CARRIER]; p != NULL; p = p->piece_link.next) {
426                 new_dist = dist (obj->loc, p->loc);
427                 if (new_dist < best_dist) {
428                         best_dist = new_dist;
429                         best_loc = p->loc;
430                 }
431         }
432         if (best_dist == 0) obj->moved += 1; /* fighter is on a city */
433         
434         else if (best_dist <= obj->range)
435                 move_to_dest (obj, best_loc);
436                 
437         else obj->func = NOFUNC; /* can't reach city or carrier */
438 }
439
440 /*
441 Move a piece in the specified direction if possible.
442 If the object is a fighter which has travelled for half its range,
443 we wake it up.
444 */
445
446 void move_dir (obj)
447 piece_info_t *obj;
448 {
449         long loc;
450         int dir;
451
452         dir = MOVE_DIR (obj->func);
453         loc = obj->loc + dir_offset[dir];
454
455         if (good_loc (obj, loc))
456                 move_obj (obj, loc);
457 }
458
459 /*
460 Move a piece toward a specified destination if possible.  For each
461 direction, we see if moving in that direction would bring us closer
462 to our destination, and if there is nothing in the way.  If so, we
463 move in the first direction we find.
464 */
465
466 void move_path (obj)
467 piece_info_t *obj;
468 {
469         if (obj->loc == obj->func)
470                 obj->func = NOFUNC;
471         else move_to_dest (obj, obj->func);
472 }
473
474 /*
475 Move a piece toward a specific destination.  We first map out
476 the paths to the destination, if we can't get there, we return.
477 Then we mark the paths to the destination.  Then we choose a
478 move.
479 */
480
481 void move_to_dest (obj, dest)
482 piece_info_t *obj;
483 long dest;
484 {
485         path_map_t path_map[MAP_SIZE];
486         int fterrain;
487         char *mterrain;
488         long new_loc;
489         
490         switch (obj->type) {
491         case ARMY:
492                 fterrain = T_LAND;
493                 mterrain = "+";
494                 break;
495         case FIGHTER:
496                 fterrain = T_AIR;
497                 mterrain = "+.O";
498                 break;
499         default:
500                 fterrain = T_WATER;
501                 mterrain = ".O";
502                 break;
503         }
504         
505         new_loc = vmap_find_dest (path_map, user_map, obj->loc, dest,
506                                   USER, fterrain);
507         if (new_loc == obj->loc) return; /* can't get there */
508         
509         vmap_mark_path (path_map, user_map, dest);
510         new_loc = vmap_find_dir (path_map, user_map, obj->loc, mterrain, " .");
511         if (new_loc == obj->loc) return; /* can't move ahead */
512         ASSERT (good_loc (obj, new_loc));
513         move_obj (obj, new_loc); /* everything looks good */
514 }
515
516 /*
517 Ask the user to move her piece.
518 */
519
520 void ask_user (obj)
521 piece_info_t *obj;
522 {
523         void user_skip(), user_fill(), user_dir(), user_set_dir();
524         void user_wake(), user_set_city_func(), user_cancel_auto();
525         void user_redraw(), user_random(), user_land(), user_sentry();
526         void user_help(), reset_func(), user_explore();
527         void user_build(), user_transport();
528         void user_armyattack(), user_repair();
529
530         char c;
531
532     for (;;) {
533         display_loc_u (obj->loc); /* display piece to move */
534         describe_obj (obj); /* describe object to be moved */
535         display_score (); /* show current score */
536         display_loc_u (obj->loc); /* reposition cursor */
537
538         c = get_chx (); /* get command from user (no echo) */
539         switch (c) {
540         case 'Q': user_dir (obj, NORTHWEST); return;
541         case 'W': user_dir (obj, NORTH); return;
542         case 'E': user_dir (obj, NORTHEAST); return;
543         case 'D': user_dir (obj, EAST); return;
544         case 'C': user_dir (obj, SOUTHEAST); return;
545         case 'X': user_dir (obj, SOUTH); return;
546         case 'Z': user_dir (obj, SOUTHWEST); return;
547         case 'A': user_dir (obj, WEST); return;
548
549         case 'J': edit (obj->loc); reset_func (obj); return;
550         case 'V': user_set_city_func (obj); reset_func (obj); return;
551         
552         case ' ': user_skip (obj); return;
553         case 'F': user_fill (obj); return;
554         case 'I': user_set_dir (obj); return;
555         case 'R': user_random (obj); return;
556         case 'S': user_sentry (obj); return;
557         case 'L': user_land (obj); return;
558         case 'G': user_explore (obj); return;
559         case 'T': user_transport (obj); return;
560         case 'U': user_repair (obj); return;
561         case 'Y': user_armyattack (obj); return;
562
563         case 'B': user_build (obj); break;
564         case 'H': user_help (); break;
565         case 'K': user_wake (obj); break;
566         case 'O': user_cancel_auto (); break;
567         case '\014':
568         case 'P': user_redraw (); break;
569         case '?': describe_obj (obj); break;
570
571         default: (void) beep ();
572         }
573     }
574 }
575
576 /*
577 Here, if the passed object is on a city, we assign
578 the city's function to the object.  However, we then awaken the
579 object if necessary because the user only changed the city
580 function, and did not tell us what to do with the object.
581 */
582
583 void
584 reset_func (obj)
585 piece_info_t *obj;
586 {
587         city_info_t *cityp;
588         
589         cityp = find_city (obj->loc);
590
591         if (cityp != NULL)
592         if (cityp->func[obj->type] != NOFUNC) {
593                 obj->func = cityp->func[obj->type];
594                 (void) awake (obj);
595         } 
596 }
597
598 /*
599 Increment the number of moves a piece has used.  If the piece
600 is an army and the army is in a city, move the army to
601 the city.
602 */
603
604 void
605 user_skip (obj)
606 piece_info_t *obj;
607 {
608         if (obj->type == ARMY && user_map[obj->loc].contents == 'O')
609                 move_army_to_city (obj, obj->loc);
610         else obj->moved++;
611 }
612
613 /*
614 Put an object in FILL mode.  The object must be either a transport
615 or carrier.  If not, we beep at the user.
616 */
617
618 void
619 user_fill (obj)
620 piece_info_t *obj;
621 {
622         if (obj->type != TRANSPORT && obj->type != CARRIER) (void) beep ();
623         else obj->func = FILL;
624 }
625
626 /*
627 Print out help information.
628 */
629
630 void
631 user_help (void) {
632         char c;
633
634         help (help_user, user_lines);
635         prompt ("Press any key to continue: ",0,0,0,0,0,0,0,0);
636         c = get_chx ();
637         c = c; /* keep lint happy */
638 }
639
640 /*
641 Set an object's function to move in a certain direction.
642 */
643
644 void
645 user_set_dir (obj)
646 piece_info_t *obj;
647 {
648         char c;
649
650         c = get_chx ();
651         switch (c) {
652         case 'Q': obj->func = MOVE_NW; break;
653         case 'W': obj->func = MOVE_N ; break;
654         case 'E': obj->func = MOVE_NE; break;
655         case 'D': obj->func = MOVE_E ; break;
656         case 'C': obj->func = MOVE_SE; break;
657         case 'X': obj->func = MOVE_S ; break;
658         case 'Z': obj->func = MOVE_SW; break;
659         case 'A': obj->func = MOVE_W ; break;
660         default: (void) beep (); break;
661         }
662 }
663
664 /*
665 Wake up the current piece.
666 */
667
668 void
669 user_wake (obj)
670 piece_info_t *obj;
671 {
672         obj->func = NOFUNC;
673 }
674
675 /*
676 Set the piece's func to random.  
677 */
678
679 void
680 user_random (obj)
681 piece_info_t *obj;
682 {
683         obj->func = RANDOM;
684 }
685
686 /*
687 Set a piece's function to sentry.
688 */
689
690 void
691 user_sentry (obj)
692 piece_info_t *obj;
693 {
694         obj->func = SENTRY;
695 }
696
697 /*
698 Set a fighter's function to land at the nearest city.
699 */
700
701 void
702 user_land (obj)
703 piece_info_t *obj;
704 {
705         if (obj->type != FIGHTER) (void) beep ();
706         else obj->func = LAND;
707 }
708
709 /*
710 Set the piece's func to explore.
711 */
712
713 void
714 user_explore (obj)
715 piece_info_t *obj;
716 {
717         obj->func = EXPLORE;
718 }
719
720 /*
721 Set an army's function to WFTRANSPORT.
722 */
723
724 void
725 user_transport (obj)
726 piece_info_t *obj;
727 {
728         if (obj->type != ARMY) (void) beep ();
729         else obj->func = WFTRANSPORT;
730 }
731
732 /*
733 Set an army's function to ARMYATTACK.
734 */
735
736 void
737 user_armyattack (obj)
738 piece_info_t *obj;
739 {
740         if (obj->type != ARMY) (void) beep ();
741         else obj->func = ARMYATTACK;
742 }
743
744 /*
745 Set a ship's function to REPAIR.
746 */
747
748 void
749 user_repair (obj)
750 piece_info_t *obj;
751 {
752         if (obj->type == ARMY || obj->type == FIGHTER) (void) beep ();
753         else obj->func = REPAIR;
754 }
755
756 /*
757 Set a city's function.
758 */
759
760 void
761 user_set_city_func (obj)
762 piece_info_t *obj;
763 {
764         void e_city_fill(), e_city_explore(), e_city_stasis();
765         void e_city_wake(), e_city_random(), e_city_repair();
766         void e_city_attack();
767         
768         int type;
769         char e;
770         city_info_t *cityp;
771
772         cityp = find_city (obj->loc);
773         if (!cityp || cityp->owner != USER) {
774                 (void) beep ();
775                 return;
776         }
777
778         type = get_piece_name();
779         if (type == NOPIECE) {
780                 (void) beep ();
781                 return;
782         }
783         
784         e = get_chx ();
785         
786         switch (e) {
787         case 'F': /* fill */
788                 e_city_fill (cityp, type);
789                 break;
790         case 'G': /* explore */
791                 e_city_explore (cityp, type);
792                 break;
793         case 'I': /* directional stasis */
794                 e_city_stasis (cityp, type);
795                 break;
796         case 'K': /* turn off function */
797                 e_city_wake (cityp, type);
798                 break;
799         case 'R': /* make piece move randomly */
800                 e_city_random (cityp, type);
801                 break;
802         case 'U': /* repair ship */
803                 e_city_repair (cityp, type);
804                 break;
805         case 'Y': /* set army func to attack */
806                 e_city_attack (cityp, type);
807                 break;
808         default: /* bad command? */
809                 (void) beep ();
810                 break;
811         }
812 }
813
814 /*
815 Change a city's production.
816 */
817
818 void
819 user_build (obj)
820 piece_info_t *obj;
821 {
822         city_info_t *cityp;
823
824         if (user_map[obj->loc].contents != 'O') { /* no user city here? */
825                 (void) beep ();
826                 return;
827         }
828         cityp = find_city (obj->loc);
829         ASSERT (cityp != NULL);
830         set_prod (cityp);
831 }
832
833 /*
834 Move a piece in the direction specified by the user.
835 This routine handles attacking objects.
836 */
837
838 void
839 user_dir (obj, dir)
840 piece_info_t *obj;
841 int dir;
842 {
843         void user_dir_army(), user_dir_fighter(), user_dir_ship();
844
845         long loc;
846
847         loc = obj->loc + dir_offset[dir];
848
849         if (good_loc (obj, loc)) {
850                 move_obj (obj, loc);
851                 return;
852         }
853         if (!map[loc].on_board) {
854                 error ("You cannot move to the edge of the world.",0,0,0,0,0,0,0,0);
855                 delay ();
856                 return;
857         }
858         switch (obj->type) {
859         case ARMY: user_dir_army (obj, loc); break;
860         case FIGHTER: user_dir_fighter (obj, loc); break;
861         default: user_dir_ship (obj, loc); break;
862         }
863 }
864
865 /*
866 We have an army that wants to attack something or move onto some
867 unreasonable terrain.  We check for errors, question the user if
868 necessary, and attack if necessary.
869 */
870
871 void
872 user_dir_army (obj, loc)
873 piece_info_t *obj;
874 long loc;
875 {
876         int enemy_killed;
877         
878         enemy_killed = FALSE;
879
880         if (user_map[loc].contents == 'O') /* attacking own city */
881                 move_army_to_city (obj, loc);
882
883         else if (user_map[loc].contents == 'T') /* transport full? */
884                 fatal (obj, loc,
885         "Sorry, sir.  There is no more room on the transport.  Do you insist? ",
886         "Your army jumped into the briny and drowned.");
887
888         else if (map[loc].contents == '.') { /* going for a swim? */
889                 if (!getyn ( /* thanks to Craig Hansen for this next message */
890         "Troops can't walk on water, sir.  Do you really want to go to sea? "))
891                 return;
892
893                 if (user_map[obj->loc].contents == 'T')
894                 {
895                         comment ("Your army jumped into the briny and drowned.",0,0,0,0,0,0,0,0);
896                         ksend ("Your army jumped into the briny and drowned.\n",0,0,0,0,0,0,0,0);
897                 }
898                 else if (user_map[loc].contents == '.')
899                 {
900                         comment ("Your army marched dutifully into the sea and drowned.",0,0,0,0,0,0,0,0);
901                         ksend ("Your army marched dutifully into the sea and drowned.\n",0,0,0,0,0,0,0,0);
902                 }
903                 else { /* attack something at sea */
904                         enemy_killed = islower (user_map[loc].contents);
905                         attack (obj, loc);
906         
907                         if (obj->hits > 0) /* ship won? */
908                         {
909                                 comment ("Your army regretfully drowns after its successful assault.",0,0,0,0,0,0,0,0);
910                                 ksend ("Your army regretfully drowns after it's successful assault.",0,0,0,0,0,0,0,0);
911                         }
912                 }
913                 if (obj->hits > 0) {
914                         kill_obj (obj, loc);
915                         if (enemy_killed) scan (comp_map, loc);
916                 }
917         }
918                 
919         else if (isupper (user_map[loc].contents)
920                 && user_map[loc].contents != 'X') { /* attacking self */
921                 if (!getyn (
922         "Sir, those are our men!  Do you really want to attack them? "))
923                 return;
924
925                 attack (obj, loc);
926         }
927
928         else attack (obj, loc);
929 }
930
931 /*
932 Here we have a fighter wanting to attack something.  There are only
933 three cases:  attacking a city, attacking ourself, attacking the enemy.
934 */
935
936 void
937 user_dir_fighter (obj, loc)
938 piece_info_t *obj;
939 long loc;
940 {
941         if (map[loc].contents == '*')
942                 fatal (obj, loc,
943         "That's never worked before, sir.  Do you really want to try? ",
944         "Your fighter was shot down.");
945
946         else if (isupper (user_map[loc].contents)) {
947                 if (!getyn (
948         "Sir, those are our men!  Do you really want to attack them? "))
949                 return;
950
951                 attack (obj, loc);
952         }
953
954         else attack (obj, loc);
955 }
956
957 /*
958 Here we have a ship attacking something, or trying to move on
959 shore.  Our cases are: moving ashore (and subcases), attacking
960 a city, attacking self, attacking enemy.
961 */
962         
963 void
964 user_dir_ship (obj, loc)
965 piece_info_t *obj;
966 long loc;
967 {
968         int enemy_killed;
969
970         enemy_killed = FALSE;
971
972         if (map[loc].contents == '*') {
973                 (void) sprintf (jnkbuf, "Your %s broke up on shore.",
974                                 piece_attr[obj->type].name);
975
976                 fatal (obj, loc,
977         "That's never worked before, sir.  Do you really want to try? ",
978                         jnkbuf);
979         }
980
981         else if (map[loc].contents == '+') { /* moving ashore? */
982                 if (!getyn ("Ships need sea to float, sir.  Do you really want to go ashore? ")) return;
983
984                 if (user_map[loc].contents == '+')
985                 {
986                         comment1 ("Your %s broke up on shore.", piece_attr[obj->type].name,0,0,0,0,0,0,0);
987                         ksend1 ("Your %s broke up on shore.", piece_attr[obj->type].name,0,0,0,0,0,0,0);
988                 }
989                 else { /* attack something on shore */
990                         enemy_killed = islower (user_map[loc].contents);
991                         attack (obj, loc);
992
993                         if (obj->hits > 0) /* ship won? */
994                         {
995                                 comment1 ("Your %s breaks up after its successful assault.", piece_attr[obj->type].name,0,0,0,0,0,0,0);
996                                 ksend1 ("Your %s breaks up after its successful assault.", piece_attr[obj->type].name,0,0,0,0,0,0,0);
997                         }
998                 }
999                 if (obj->hits > 0) {
1000                         kill_obj (obj, loc);
1001                         if (enemy_killed) scan (comp_map, loc);
1002                 }
1003         }
1004                 
1005         else if (isupper (user_map[loc].contents)) { /* attacking self */
1006                 if (!getyn (
1007         "Sir, those are our men!  Do you really want to attack them? "))
1008                 return;
1009
1010                 attack (obj, loc);
1011         }
1012
1013         else attack (obj, loc);
1014 }
1015
1016 /*
1017 Here a user wants to move an army to a city.  If the city contains
1018 a non-full transport, we make the move.  Otherwise we ask the user
1019 if she really wants to attack the city.
1020 */
1021
1022 void
1023 move_army_to_city (obj, city_loc)
1024 piece_info_t *obj;
1025 long city_loc;
1026 {
1027         piece_info_t *tt;
1028
1029         tt = find_nfull (TRANSPORT, city_loc);
1030
1031         if (tt != NULL) move_obj (obj, city_loc);
1032
1033         else fatal (obj, city_loc,
1034         "That's our city, sir!  Do you really want to attack the garrison? ",
1035         "Your rebel army was liquidated.");
1036 }
1037
1038 /*
1039 Cancel automove mode.
1040 */
1041
1042 void
1043 user_cancel_auto (void) {
1044         if (!automove)
1045                 comment ("Not in auto mode!",0,0,0,0,0,0,0,0);
1046         else {
1047                 automove = FALSE;
1048                 comment ("Auto mode cancelled.",0,0,0,0,0,0,0,0);
1049         }
1050 }
1051
1052 /*
1053 Redraw the screen.
1054 */
1055
1056 void
1057 user_redraw (void) {
1058         redraw ();
1059 }
1060
1061 /*
1062 Awaken an object if it needs to be.  Normally, objects are awakened
1063 when they are next to an enemy object or an unowned city.  Armies
1064 on troop transports are not awakened if they are surrounded by sea.
1065 We return true if the object is now awake.  Objects are never
1066 completely awoken here if their function is a destination.  But we
1067 will return TRUE if we want the user to have control.
1068 */
1069
1070 int
1071 awake (obj)
1072 piece_info_t *obj;
1073 {
1074         int i;
1075         char c;
1076         long t;
1077
1078         if (obj->type == ARMY && vmap_at_sea (user_map, obj->loc)) {
1079             obj->moved = piece_attr[ARMY].range;
1080             return (FALSE);
1081         }
1082         if (obj->func == NOFUNC) return (TRUE); /* object is awake */
1083         
1084         if (obj->type == FIGHTER /* wake fighters */
1085             && obj->func != LAND /* that aren't returning to base */
1086             && obj->func < 0 /* and which don't have a path */
1087             && obj->range <= find_nearest_city (obj->loc, USER, &t) + 2) {
1088                 obj->func = NOFUNC; /* wake piece */
1089                 return (TRUE);
1090         }
1091         for (i = 0; i < 8; i++) { /* for each surrounding cell */
1092                 c = user_map[obj->loc+dir_offset[i]].contents;
1093
1094                 if (islower (c) || c == '*' || c == 'X') {
1095                         if (obj->func < 0) obj->func = NOFUNC; /* awaken */
1096                         return (TRUE);
1097                 }
1098         }
1099         return (FALSE);
1100 }
1101
1102 /*
1103 Question the user about a fatal move.  If the user responds 'y',
1104 print out the response and kill the object.
1105 */
1106
1107 void
1108 fatal (obj, loc, message, response)
1109 piece_info_t *obj;
1110 long loc;
1111 char *message;
1112 char *response;
1113 {
1114         if (getyn (message)) {
1115                 comment (response,0,0,0,0,0,0,0,0);
1116                 kill_obj (obj, loc);
1117         }
1118 }