More type cleanliness; introducing count_t.
[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 (obj)
106 piece_info_t *obj;
107 {
108         void move_random(), move_fill(), move_land(), move_explore();
109         void move_path(), move_dir(), move_armyload(), ask_user();
110         void move_armyattack(), move_ttload(), move_repair();
111         void move_transport();
112
113         int changed_loc;
114         int speed, max_hits;
115         int saved_moves;
116         int need_input;
117         loc_t saved_loc;
118         city_info_t *cityp;
119
120         /* set func for piece if on city */
121         cityp = find_city (obj->loc);
122         if (cityp != NULL)
123                 if (cityp->func[obj->type] != NOFUNC)
124                         obj->func = cityp->func[obj->type];
125
126         changed_loc = FALSE; /* not changed yet */
127         speed = piece_attr[obj->type].speed;
128         max_hits = piece_attr[obj->type].max_hits;
129         need_input = FALSE; /* don't require user input yet */
130
131         while (obj->moved < obj_moves (obj)) {
132                 saved_moves = obj->moved; /* save moves made */
133                 saved_loc = obj->loc; /* remember starting location */
134
135                 if (awake (obj) || need_input){ /* need user input? */
136                         ask_user (obj);
137                         topini (); /* clear info lines */
138                         display_loc_u (obj->loc); /* let user see result */
139                         (void) refresh ();
140                         need_input = FALSE; /* we got it */
141                 }
142                 
143                 if (obj->moved == saved_moves) /* user set function? */
144                 switch (obj->func) { /* handle preprogrammed function */
145                 case NOFUNC:    break;
146                 case RANDOM:    move_random (obj); break;
147                 case SENTRY:    obj->moved = speed; break;
148                 case FILL:      move_fill (obj); break;
149                 case LAND:      move_land (obj); break;
150                 case EXPLORE:   move_explore (obj); break;
151                 case ARMYLOAD:  move_armyload (obj); break;
152                 case ARMYATTACK:move_armyattack (obj); break;
153                 case TTLOAD:    move_ttload (obj); break;
154                 case REPAIR:    move_repair (obj); break;
155                 case WFTRANSPORT: move_transport (obj); break;
156
157                 case MOVE_N:
158                 case MOVE_NE:
159                 case MOVE_E:
160                 case MOVE_SE:
161                 case MOVE_S:
162                 case MOVE_SW:
163                 case MOVE_W:
164                 case MOVE_NW:
165                         move_dir (obj); break;
166
167                 default: move_path (obj); break;
168                 }
169
170                 if (obj->moved == saved_moves) need_input = TRUE;
171                 
172                 /* handle fighters specially.  If in a city or carrier, turn
173                 is over and reset range to max.  Otherwise, if
174                 range = 0, fighter crashes and burns and turn is over. */
175
176                 if (obj->type == FIGHTER && obj->hits > 0) {
177                         if ((user_map[obj->loc].contents == 'O'
178                           || user_map[obj->loc].contents == 'C')
179                         && obj->moved > 0) {
180                                 obj->range = piece_attr[FIGHTER].range;
181                                 obj->moved = speed;
182                                 obj->func = NOFUNC;
183                                 comment ("Landing confirmed.",0,0,0,0,0,0,0,0);
184                         }
185                         else if (obj->range == 0) {
186                                 comment ("Fighter at %d crashed and burned.",loc_disp(obj->loc),0,0,0,0,0,0,0);
187                                 kill_obj (obj, obj->loc);
188                         }
189                 }
190
191                 if (saved_loc != obj->loc) changed_loc = TRUE;
192         }
193         /* if a boat is in port, damaged, and never moved, fix some damage */
194         if (obj->hits > 0 /* still alive? */
195                 && !changed_loc /* object never changed location? */
196                 && obj->type != ARMY && obj->type != FIGHTER /* it is a boat? */
197                 && obj->hits < max_hits /* it is damaged? */
198                 && user_map[obj->loc].contents == 'O') /* it is in port? */
199         obj->hits++; /* fix some damage */
200 }
201
202 /*
203 Move a piece at random.  We create a list of empty squares to which
204 the piece can move.  If there are none, we do nothing, otherwise we 
205 move the piece to a random adjacent square.
206 */
207
208 void move_random (obj)
209 piece_info_t *obj;
210 {
211         loc_t loc_list[8];
212         int i, nloc;
213         loc_t loc;
214
215         nloc = 0;
216
217         for (i = 0; i < 8; i++) {
218                 loc = obj->loc + dir_offset[i];
219                 if (good_loc (obj, loc)) {
220                         loc_list[nloc] = loc; /* remember this location */
221                         nloc++; /* count locations we can move to */
222                 }
223         }
224         if (nloc == 0) return; /* no legal move */
225         i = irand ((long)nloc-1); /* choose random direction */
226         move_obj (obj, loc_list[i]); /* move the piece */
227 }
228
229 /*
230 Have a piece explore.  We look for the nearest unexplored territory
231 which the piece can reach and have to piece move toward the
232 territory.
233 */
234
235 void move_explore (obj)
236 piece_info_t *obj;
237 {
238         path_map_t path_map[MAP_SIZE];
239         loc_t loc;
240         char *terrain;
241
242         switch (obj->type) {
243         case ARMY:
244                 loc = vmap_find_lobj (path_map, user_map, obj->loc, &user_army);
245                 terrain = "+";
246                 break;
247         case FIGHTER:
248                 loc = vmap_find_aobj (path_map, user_map, obj->loc, &user_fighter);
249                 terrain = "+.O";
250                 break;
251         default:
252                 loc = vmap_find_wobj (path_map, user_map, obj->loc, &user_ship);
253                 terrain = ".O";
254                 break;
255         }
256         
257         if (loc == obj->loc) return; /* nothing to explore */
258
259         if (user_map[loc].contents == ' ' && path_map[loc].cost == 2)
260                 vmap_mark_adjacent (path_map, obj->loc);
261         else vmap_mark_path (path_map, user_map, loc);
262
263         loc = vmap_find_dir (path_map, user_map, obj->loc, terrain, " ");
264         if (loc != obj->loc) move_obj (obj, loc);
265 }
266
267 /*
268 Move an army onto a transport when it arrives.  We scan around the
269 army to find a non-full transport.  If one is present, we move the
270 army to the transport and waken the army.
271 */
272
273 void
274 move_transport (obj)
275 piece_info_t *obj;
276 {
277         loc_t loc;
278
279         /* look for an adjacent transport */
280         loc = find_transport (USER, obj->loc);
281         
282         if (loc != obj->loc) {
283                 move_obj (obj, loc);
284                 obj->func = NOFUNC;
285         }
286         else obj->moved = piece_attr[obj->type].speed;
287 }
288
289 /*
290 Move an army toward the nearest loading transport.
291 If there is an adjacent transport, move the army onto
292 the transport, and awaken the army.
293 */
294
295 static view_map_t amap[MAP_SIZE];
296
297 void
298 move_armyload (obj)
299 piece_info_t *obj;
300 {
301         loc_t loc;
302         piece_info_t *p;
303         int i;
304
305         ABORT;
306         
307         /* look for an adjacent transport */
308         loc = find_transport (USER, obj->loc);
309
310         if (loc != obj->loc) {
311                 move_obj (obj, loc);
312                 obj->func = NOFUNC;
313         }
314         else { /* look for nearest non-full transport */
315                 (void) memcpy (amap, user_map, sizeof (view_map_t) * MAP_SIZE);
316
317                 /* mark loading transports or cities building transports */
318                 for (p = user_obj[TRANSPORT]; p; p = p->piece_link.next)
319                 if (p->count < obj_capacity (p)) /* not full? */
320                 amap[p->loc].contents = '$';
321                 
322                 for (i = 0; i < NUM_CITY; i++)
323                 if (city[i].owner == USER && city[i].prod == TRANSPORT)
324                 amap[city[i].loc].contents = '$';
325         }
326 }
327                 
328 /*
329 Move an army toward an attackable city or enemy army.
330 */
331
332 void
333 move_armyattack (obj)
334 piece_info_t *obj;
335 {
336         path_map_t path_map[MAP_SIZE];
337         loc_t loc;
338
339         ASSERT (obj->type == ARMY);
340
341         loc = vmap_find_lobj (path_map, user_map, obj->loc, &user_army_attack);
342         
343         if (loc == obj->loc) return; /* nothing to attack */
344
345         vmap_mark_path (path_map, user_map, loc);
346
347         loc = vmap_find_dir (path_map, user_map, obj->loc, "+", "X*a");
348         if (loc != obj->loc) move_obj (obj, loc);
349 }
350
351 void
352 move_ttload (obj)
353 piece_info_t *obj;
354 {
355         ABORT;
356         obj = obj;
357 }
358
359 /*
360 Move a ship toward port.  If the ship is healthy, wake it up.
361 */
362
363 void
364 move_repair (obj)
365 piece_info_t *obj;
366 {
367         path_map_t path_map[MAP_SIZE];
368         loc_t loc;
369
370         ASSERT (obj->type > FIGHTER);
371         
372         if (obj->hits == piece_attr[obj->type].max_hits) {
373                 obj->func = NOFUNC;
374                 return;
375         }
376         
377         if (user_map[obj->loc].contents == 'O') { /* it is in port? */
378                 obj->moved += 1;
379                 return;
380         }
381
382         loc = vmap_find_wobj (path_map, user_map, obj->loc, &user_ship_repair);
383         
384         if (loc == obj->loc) return; /* no reachable city */
385
386         vmap_mark_path (path_map, user_map, loc);
387
388         /* try to be next to ocean to avoid enemy pieces */
389         loc = vmap_find_dir (path_map, user_map, obj->loc, ".O", ".");
390         if (loc != obj->loc) move_obj (obj, loc);
391 }
392
393 /*
394 Here we have a transport or carrier waiting to be filled.  If the
395 object is not full, we set the move count to its maximum value.
396 Otherwise we awaken the object.
397 */
398
399 void move_fill (obj)
400 piece_info_t *obj;
401 {
402         if (obj->count == obj_capacity (obj)) /* full? */
403                 obj->func = NOFUNC; /* awaken full boat */
404         else obj->moved = piece_attr[obj->type].speed;
405 }
406
407 /*
408 Here we have a piece that wants to land at the nearest carrier or
409 owned city.  We scan through the lists of cities and carriers looking
410 for the closest one.  We then move toward that item's location.
411 The nearest landing field must be within the object's range.
412 */
413
414 void
415 move_land (obj)
416 piece_info_t *obj;
417 {
418         long best_dist;
419         loc_t 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         loc_t 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 loc_t dest;
484 {
485         path_map_t path_map[MAP_SIZE];
486         int fterrain;
487         char *mterrain;
488         loc_t 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         loc_t 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 loc_t 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 loc_t 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 loc_t 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 loc_t 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 loc_t 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 }