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