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