ec131a9b85cd814a259af53a2a23db0618cb758d
[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 <curses.h>     /* Ugh...shouldn't be needed here */
16 #include "empire.h"
17 #include "extern.h"
18
19 void fatal(piece_info_t *obj,loc_t loc,char *message,char *response);
20 void move_to_dest(piece_info_t *obj,loc_t dest);
21 void move_army_to_city(piece_info_t *obj,loc_t city_loc);
22 int awake(piece_info_t *obj);
23 extern int get_piece_name(void);
24
25 void
26 user_move(void) {
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                         ksend1("%s has been completed at city %d.\n", piece_attr[prod].article,loc_disp(city[i].loc),0,0,0,0,0,0);
58                                 /* kermyt end */
59                         comment1 ("%s has been completed at city %d.\n", piece_attr[prod].article,loc_disp(city[i].loc),0,0,0,0,0,0);
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                         (void) refresh (); /* 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         int changed_loc;
113         int speed, max_hits;
114         int saved_moves;
115         int 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) refresh ();
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.",0,0,0,0,0,0,0,0);
183                         }
184                         else if (obj->range == 0) {
185                                 comment ("Fighter at %d crashed and burned.",loc_disp(obj->loc),0,0,0,0,0,0,0);
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         int i;
299
300         ABORT;
301         
302         /* look for an adjacent transport */
303         loc = find_transport (USER, obj->loc);
304
305         if (loc != obj->loc) {
306                 move_obj (obj, loc);
307                 obj->func = NOFUNC;
308         }
309         else { /* look for nearest non-full transport */
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         obj = obj;
350 }
351
352 /*
353 Move a ship toward port.  If the ship is healthy, wake it up.
354 */
355
356 void
357 move_repair(piece_info_t *obj)
358 {
359         path_map_t path_map[MAP_SIZE];
360         loc_t loc;
361
362         ASSERT (obj->type > FIGHTER);
363         
364         if (obj->hits == piece_attr[obj->type].max_hits) {
365                 obj->func = NOFUNC;
366                 return;
367         }
368         
369         if (user_map[obj->loc].contents == 'O') { /* it is in port? */
370                 obj->moved += 1;
371                 return;
372         }
373
374         loc = vmap_find_wobj (path_map, user_map, obj->loc, &user_ship_repair);
375         
376         if (loc == obj->loc) return; /* no reachable city */
377
378         vmap_mark_path (path_map, user_map, loc);
379
380         /* try to be next to ocean to avoid enemy pieces */
381         loc = vmap_find_dir (path_map, user_map, obj->loc, ".O", ".");
382         if (loc != obj->loc) move_obj (obj, loc);
383 }
384
385 /*
386 Here we have a transport or carrier waiting to be filled.  If the
387 object is not full, we set the move count to its maximum value.
388 Otherwise we awaken the object.
389 */
390
391 void move_fill(piece_info_t *obj)
392 {
393         if (obj->count == obj_capacity (obj)) /* full? */
394                 obj->func = NOFUNC; /* awaken full boat */
395         else obj->moved = piece_attr[obj->type].speed;
396 }
397
398 /*
399 Here we have a piece that wants to land at the nearest carrier or
400 owned city.  We scan through the lists of cities and carriers looking
401 for the closest one.  We then move toward that item's location.
402 The nearest landing field must be within the object's range.
403 */
404
405 void
406 move_land(piece_info_t *obj)
407 {
408         long best_dist;
409         loc_t best_loc;
410         long new_dist;
411         piece_info_t *p;
412
413         best_dist = find_nearest_city (obj->loc, USER, &best_loc);
414
415         for (p = user_obj[CARRIER]; p != NULL; p = p->piece_link.next) {
416                 new_dist = dist (obj->loc, p->loc);
417                 if (new_dist < best_dist) {
418                         best_dist = new_dist;
419                         best_loc = p->loc;
420                 }
421         }
422         if (best_dist == 0) obj->moved += 1; /* fighter is on a city */
423         
424         else if (best_dist <= obj->range)
425                 move_to_dest (obj, best_loc);
426                 
427         else obj->func = NOFUNC; /* can't reach city or carrier */
428 }
429
430 /*
431 Move a piece in the specified direction if possible.
432 If the object is a fighter which has travelled for half its range,
433 we wake it up.
434 */
435
436 void move_dir(piece_info_t *obj)
437 {
438         loc_t loc;
439         int dir;
440
441         dir = MOVE_DIR (obj->func);
442         loc = obj->loc + dir_offset[dir];
443
444         if (good_loc (obj, loc))
445                 move_obj (obj, loc);
446 }
447
448 /*
449 Move a piece toward a specified destination if possible.  For each
450 direction, we see if moving in that direction would bring us closer
451 to our destination, and if there is nothing in the way.  If so, we
452 move in the first direction we find.
453 */
454
455 void move_path(piece_info_t *obj)
456 {
457         if (obj->loc == obj->func)
458                 obj->func = NOFUNC;
459         else move_to_dest (obj, obj->func);
460 }
461
462 /*
463 Move a piece toward a specific destination.  We first map out
464 the paths to the destination, if we can't get there, we return.
465 Then we mark the paths to the destination.  Then we choose a
466 move.
467 */
468
469 void move_to_dest(piece_info_t *obj, loc_t dest)
470 {
471         path_map_t path_map[MAP_SIZE];
472         int fterrain;
473         char *mterrain;
474         loc_t new_loc;
475         
476         switch (obj->type) {
477         case ARMY:
478                 fterrain = T_LAND;
479                 mterrain = "+";
480                 break;
481         case FIGHTER:
482                 fterrain = T_AIR;
483                 mterrain = "+.O";
484                 break;
485         default:
486                 fterrain = T_WATER;
487                 mterrain = ".O";
488                 break;
489         }
490         
491         new_loc = vmap_find_dest (path_map, user_map, obj->loc, dest,
492                                   USER, fterrain);
493         if (new_loc == obj->loc) return; /* can't get there */
494         
495         vmap_mark_path (path_map, user_map, dest);
496         new_loc = vmap_find_dir (path_map, user_map, obj->loc, mterrain, " .");
497         if (new_loc == obj->loc) return; /* can't move ahead */
498         ASSERT (good_loc (obj, new_loc));
499         move_obj (obj, new_loc); /* everything looks good */
500 }
501
502 /*
503 Ask the user to move her piece.
504 */
505
506 void ask_user(piece_info_t *obj)
507 {
508         void user_skip(), user_fill(), user_dir(), user_set_dir();
509         void user_wake(), user_set_city_func(), user_cancel_auto();
510         void user_redraw(), user_random(), user_land(), user_sentry();
511         void user_help(), reset_func(), user_explore();
512         void user_build(), user_transport();
513         void user_armyattack(), user_repair();
514
515         char c;
516
517     for (;;) {
518         display_loc_u (obj->loc); /* display piece to move */
519         describe_obj (obj); /* describe object to be moved */
520         display_score (); /* show current score */
521         display_loc_u (obj->loc); /* reposition cursor */
522
523         c = get_chx (); /* get command from user (no echo) */
524         switch (c) {
525         case 'Q': user_dir (obj, NORTHWEST); return;
526         case 'W': user_dir (obj, NORTH); return;
527         case 'E': user_dir (obj, NORTHEAST); return;
528         case 'D': user_dir (obj, EAST); return;
529         case 'C': user_dir (obj, SOUTHEAST); return;
530         case 'X': user_dir (obj, SOUTH); return;
531         case 'Z': user_dir (obj, SOUTHWEST); return;
532         case 'A': user_dir (obj, WEST); return;
533
534         case 'J': edit (obj->loc); reset_func (obj); return;
535         case 'V': user_set_city_func (obj); reset_func (obj); return;
536         
537         case ' ': user_skip (obj); return;
538         case 'F': user_fill (obj); return;
539         case 'I': user_set_dir (obj); return;
540         case 'R': user_random (obj); return;
541         case 'S': user_sentry (obj); return;
542         case 'L': user_land (obj); return;
543         case 'G': user_explore (obj); return;
544         case 'T': user_transport (obj); return;
545         case 'U': user_repair (obj); return;
546         case 'Y': user_armyattack (obj); return;
547
548         case 'B': user_build (obj); break;
549         case 'H': user_help (); break;
550         case 'K': user_wake (obj); break;
551         case 'O': user_cancel_auto (); break;
552         case '\014':
553         case 'P': user_redraw (); break;
554         case '?': describe_obj (obj); break;
555
556         default: (void) beep ();
557         }
558     }
559 }
560
561 /*
562 Here, if the passed object is on a city, we assign
563 the city's function to the object.  However, we then awaken the
564 object if necessary because the user only changed the city
565 function, and did not tell us what to do with the object.
566 */
567
568 void
569 reset_func(piece_info_t *obj)
570 {
571         city_info_t *cityp;
572         
573         cityp = find_city (obj->loc);
574
575         if (cityp != NULL)
576         if (cityp->func[obj->type] != NOFUNC) {
577                 obj->func = cityp->func[obj->type];
578                 (void) awake (obj);
579         } 
580 }
581
582 /*
583 Increment the number of moves a piece has used.  If the piece
584 is an army and the army is in a city, move the army to
585 the city.
586 */
587
588 void
589 user_skip(piece_info_t *obj)
590 {
591         if (obj->type == ARMY && user_map[obj->loc].contents == 'O')
592                 move_army_to_city (obj, obj->loc);
593         else obj->moved++;
594 }
595
596 /*
597 Put an object in FILL mode.  The object must be either a transport
598 or carrier.  If not, we beep at the user.
599 */
600
601 void
602 user_fill(piece_info_t *obj)
603 {
604         if (obj->type != TRANSPORT && obj->type != CARRIER) (void) beep ();
605         else obj->func = FILL;
606 }
607
608 /*
609 Print out help information.
610 */
611
612 void
613 user_help(void) {
614         char c;
615
616         help (help_user, user_lines);
617         prompt ("Press any key to continue: ",0,0,0,0,0,0,0,0);
618         c = get_chx ();
619         c = c; /* keep lint happy */
620 }
621
622 /*
623 Set an object's function to move in a certain direction.
624 */
625
626 void
627 user_set_dir(piece_info_t *obj)
628 {
629         char c;
630
631         c = get_chx ();
632         switch (c) {
633         case 'Q': obj->func = MOVE_NW; break;
634         case 'W': obj->func = MOVE_N ; break;
635         case 'E': obj->func = MOVE_NE; break;
636         case 'D': obj->func = MOVE_E ; break;
637         case 'C': obj->func = MOVE_SE; break;
638         case 'X': obj->func = MOVE_S ; break;
639         case 'Z': obj->func = MOVE_SW; break;
640         case 'A': obj->func = MOVE_W ; break;
641         default: (void) beep (); break;
642         }
643 }
644
645 /*
646 Wake up the current piece.
647 */
648
649 void
650 user_wake(piece_info_t *obj)
651 {
652         obj->func = NOFUNC;
653 }
654
655 /*
656 Set the piece's func to random.  
657 */
658
659 void
660 user_random(piece_info_t *obj)
661 {
662         obj->func = RANDOM;
663 }
664
665 /*
666 Set a piece's function to sentry.
667 */
668
669 void
670 user_sentry(piece_info_t *obj)
671 {
672         obj->func = SENTRY;
673 }
674
675 /*
676 Set a fighter's function to land at the nearest city.
677 */
678
679 void
680 user_land(piece_info_t *obj)
681 {
682         if (obj->type != FIGHTER) (void) beep ();
683         else obj->func = LAND;
684 }
685
686 /*
687 Set the piece's func to explore.
688 */
689
690 void
691 user_explore(piece_info_t *obj)
692 {
693         obj->func = EXPLORE;
694 }
695
696 /*
697 Set an army's function to WFTRANSPORT.
698 */
699
700 void
701 user_transport(piece_info_t *obj)
702 {
703         if (obj->type != ARMY) (void) beep ();
704         else obj->func = WFTRANSPORT;
705 }
706
707 /*
708 Set an army's function to ARMYATTACK.
709 */
710
711 void
712 user_armyattack(piece_info_t *obj)
713 {
714         if (obj->type != ARMY) (void) beep ();
715         else obj->func = ARMYATTACK;
716 }
717
718 /*
719 Set a ship's function to REPAIR.
720 */
721
722 void
723 user_repair(piece_info_t *obj)
724 {
725         if (obj->type == ARMY || obj->type == FIGHTER) (void) beep ();
726         else obj->func = REPAIR;
727 }
728
729 /*
730 Set a city's function.
731 */
732
733 void
734 user_set_city_func(piece_info_t *obj)
735 {
736         void e_city_fill(), e_city_explore(), e_city_stasis();
737         void e_city_wake(), e_city_random(), e_city_repair();
738         void e_city_attack();
739         
740         int type;
741         char e;
742         city_info_t *cityp;
743
744         cityp = find_city (obj->loc);
745         if (!cityp || cityp->owner != USER) {
746                 (void) beep ();
747                 return;
748         }
749
750         type = get_piece_name();
751         if (type == NOPIECE) {
752                 (void) beep ();
753                 return;
754         }
755         
756         e = get_chx ();
757         
758         switch (e) {
759         case 'F': /* fill */
760                 e_city_fill (cityp, type);
761                 break;
762         case 'G': /* explore */
763                 e_city_explore (cityp, type);
764                 break;
765         case 'I': /* directional stasis */
766                 e_city_stasis (cityp, type);
767                 break;
768         case 'K': /* turn off function */
769                 e_city_wake (cityp, type);
770                 break;
771         case 'R': /* make piece move randomly */
772                 e_city_random (cityp, type);
773                 break;
774         case 'U': /* repair ship */
775                 e_city_repair (cityp, type);
776                 break;
777         case 'Y': /* set army func to attack */
778                 e_city_attack (cityp, type);
779                 break;
780         default: /* bad command? */
781                 (void) beep ();
782                 break;
783         }
784 }
785
786 /*
787 Change a city's production.
788 */
789
790 void
791 user_build(piece_info_t *obj)
792 {
793         city_info_t *cityp;
794
795         if (user_map[obj->loc].contents != 'O') { /* no user city here? */
796                 (void) beep ();
797                 return;
798         }
799         cityp = find_city (obj->loc);
800         ASSERT (cityp != NULL);
801         set_prod (cityp);
802 }
803
804 /*
805 Move a piece in the direction specified by the user.
806 This routine handles attacking objects.
807 */
808
809 void
810 user_dir(piece_info_t *obj, int dir)
811 {
812         void user_dir_army(), user_dir_fighter(), user_dir_ship();
813
814         loc_t loc;
815
816         loc = obj->loc + dir_offset[dir];
817
818         if (good_loc (obj, loc)) {
819                 move_obj (obj, loc);
820                 return;
821         }
822         if (!map[loc].on_board) {
823                 error ("You cannot move to the edge of the world.",0,0,0,0,0,0,0,0);
824                 delay ();
825                 return;
826         }
827         switch (obj->type) {
828         case ARMY: user_dir_army (obj, loc); break;
829         case FIGHTER: user_dir_fighter (obj, loc); break;
830         default: user_dir_ship (obj, loc); break;
831         }
832 }
833
834 /*
835 We have an army that wants to attack something or move onto some
836 unreasonable terrain.  We check for errors, question the user if
837 necessary, and attack if necessary.
838 */
839
840 void
841 user_dir_army(piece_info_t *obj, loc_t loc)
842 {
843         int enemy_killed;
844         
845         enemy_killed = FALSE;
846
847         if (user_map[loc].contents == 'O') /* attacking own city */
848                 move_army_to_city (obj, loc);
849
850         else if (user_map[loc].contents == 'T') /* transport full? */
851                 fatal (obj, loc,
852         "Sorry, sir.  There is no more room on the transport.  Do you insist? ",
853         "Your army jumped into the briny and drowned.");
854
855         else if (map[loc].contents == '.') { /* going for a swim? */
856                 if (!getyn ( /* thanks to Craig Hansen for this next message */
857         "Troops can't walk on water, sir.  Do you really want to go to sea? "))
858                 return;
859
860                 if (user_map[obj->loc].contents == 'T')
861                 {
862                         comment ("Your army jumped into the briny and drowned.",0,0,0,0,0,0,0,0);
863                         ksend ("Your army jumped into the briny and drowned.\n",0,0,0,0,0,0,0,0);
864                 }
865                 else if (user_map[loc].contents == '.')
866                 {
867                         comment ("Your army marched dutifully into the sea and drowned.",0,0,0,0,0,0,0,0);
868                         ksend ("Your army marched dutifully into the sea and drowned.\n",0,0,0,0,0,0,0,0);
869                 }
870                 else { /* attack something at sea */
871                         enemy_killed = islower (user_map[loc].contents);
872                         attack (obj, loc);
873         
874                         if (obj->hits > 0) /* ship won? */
875                         {
876                                 comment ("Your army regretfully drowns after its successful assault.",0,0,0,0,0,0,0,0);
877                                 ksend ("Your army regretfully drowns after it's successful assault.",0,0,0,0,0,0,0,0);
878                         }
879                 }
880                 if (obj->hits > 0) {
881                         kill_obj (obj, loc);
882                         if (enemy_killed) scan (comp_map, loc);
883                 }
884         }
885                 
886         else if (isupper (user_map[loc].contents)
887                 && user_map[loc].contents != 'X') { /* attacking self */
888                 if (!getyn (
889         "Sir, those are our men!  Do you really want to attack them? "))
890                 return;
891
892                 attack (obj, loc);
893         }
894
895         else attack (obj, loc);
896 }
897
898 /*
899 Here we have a fighter wanting to attack something.  There are only
900 three cases:  attacking a city, attacking ourself, attacking the enemy.
901 */
902
903 void
904 user_dir_fighter(piece_info_t *obj, loc_t loc)
905 {
906         if (map[loc].contents == '*')
907                 fatal (obj, loc,
908         "That's never worked before, sir.  Do you really want to try? ",
909         "Your fighter was shot down.");
910
911         else if (isupper (user_map[loc].contents)) {
912                 if (!getyn (
913         "Sir, those are our men!  Do you really want to attack them? "))
914                 return;
915
916                 attack (obj, loc);
917         }
918
919         else attack (obj, loc);
920 }
921
922 /*
923 Here we have a ship attacking something, or trying to move on
924 shore.  Our cases are: moving ashore (and subcases), attacking
925 a city, attacking self, attacking enemy.
926 */
927         
928 void
929 user_dir_ship(piece_info_t *obj, loc_t loc)
930 {
931         int enemy_killed;
932
933         enemy_killed = FALSE;
934
935         if (map[loc].contents == '*') {
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 == '+') { /* moving ashore? */
945                 if (!getyn ("Ships need sea to float, sir.  Do you really want to go ashore? ")) return;
946
947                 if (user_map[loc].contents == '+')
948                 {
949                         comment1 ("Your %s broke up on shore.", piece_attr[obj->type].name,0,0,0,0,0,0,0);
950                         ksend1 ("Your %s broke up on shore.", piece_attr[obj->type].name,0,0,0,0,0,0,0);
951                 }
952                 else { /* attack something on shore */
953                         enemy_killed = islower (user_map[loc].contents);
954                         attack (obj, loc);
955
956                         if (obj->hits > 0) /* ship won? */
957                         {
958                                 comment1 ("Your %s breaks up after its successful assault.", piece_attr[obj->type].name,0,0,0,0,0,0,0);
959                                 ksend1 ("Your %s breaks up after its successful assault.", piece_attr[obj->type].name,0,0,0,0,0,0,0);
960                         }
961                 }
962                 if (obj->hits > 0) {
963                         kill_obj (obj, loc);
964                         if (enemy_killed) scan (comp_map, loc);
965                 }
966         }
967                 
968         else if (isupper (user_map[loc].contents)) { /* attacking self */
969                 if (!getyn (
970         "Sir, those are our men!  Do you really want to attack them? "))
971                 return;
972
973                 attack (obj, loc);
974         }
975
976         else attack (obj, loc);
977 }
978
979 /*
980 Here a user wants to move an army to a city.  If the city contains
981 a non-full transport, we make the move.  Otherwise we ask the user
982 if she really wants to attack the city.
983 */
984
985 void
986 move_army_to_city(piece_info_t *obj, loc_t city_loc)
987 {
988         piece_info_t *tt;
989
990         tt = find_nfull (TRANSPORT, city_loc);
991
992         if (tt != NULL) move_obj (obj, city_loc);
993
994         else fatal (obj, city_loc,
995         "That's our city, sir!  Do you really want to attack the garrison? ",
996         "Your rebel army was liquidated.");
997 }
998
999 /*
1000 Cancel automove mode.
1001 */
1002
1003 void
1004 user_cancel_auto(void)
1005 {
1006         if (!automove)
1007                 comment ("Not in auto mode!",0,0,0,0,0,0,0,0);
1008         else {
1009                 automove = FALSE;
1010                 comment ("Auto mode cancelled.",0,0,0,0,0,0,0,0);
1011         }
1012 }
1013
1014 /*
1015 Redraw the screen.
1016 */
1017
1018 void
1019 user_redraw (void)
1020 {
1021         redraw ();
1022 }
1023
1024 /*
1025 Awaken an object if it needs to be.  Normally, objects are awakened
1026 when they are next to an enemy object or an unowned city.  Armies
1027 on troop transports are not awakened if they are surrounded by sea.
1028 We return true if the object is now awake.  Objects are never
1029 completely awoken here if their function is a destination.  But we
1030 will return TRUE if we want the user to have control.
1031 */
1032
1033 int
1034 awake(piece_info_t *obj)
1035 {
1036         int i;
1037         char c;
1038         long t;
1039
1040         if (obj->type == ARMY && vmap_at_sea (user_map, obj->loc)) {
1041             obj->moved = piece_attr[ARMY].range;
1042             return (FALSE);
1043         }
1044         if (obj->func == NOFUNC) return (TRUE); /* object is awake */
1045         
1046         if (obj->type == FIGHTER /* wake fighters */
1047             && obj->func != LAND /* that aren't returning to base */
1048             && obj->func < 0 /* and which don't have a path */
1049             && obj->range <= find_nearest_city (obj->loc, USER, &t) + 2) {
1050                 obj->func = NOFUNC; /* wake piece */
1051                 return (TRUE);
1052         }
1053         for (i = 0; i < 8; i++) { /* for each surrounding cell */
1054                 c = user_map[obj->loc+dir_offset[i]].contents;
1055
1056                 if (islower (c) || c == '*' || c == 'X') {
1057                         if (obj->func < 0) obj->func = NOFUNC; /* awaken */
1058                         return (TRUE);
1059                 }
1060         }
1061         return (FALSE);
1062 }
1063
1064 /*
1065 Question the user about a fatal move.  If the user responds 'y',
1066 print out the response and kill the object.
1067 */
1068
1069 void
1070 fatal(piece_info_t *obj, loc_t loc, char *message, char *response)
1071 {
1072         if (getyn (message)) {
1073                 comment (response,0,0,0,0,0,0,0,0);
1074                 kill_obj (obj, loc);
1075         }
1076 }