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