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