1 /* Commands for the curses interface to Xconq.
2 Copyright (C) 1986-1989, 1991-2000 Stanley T. Shebs.
3
4 Xconq is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version. See the file COPYING. */
8
9 #include "conq.h"
10 #include "kpublic.h"
11 #include "cconq.h"
12 #include "aiunit.h"
13 #include "aiunit2.h"
14
15 extern int drawlinear;
16 extern char linear_char;
17 extern char bord_char;
18 extern char conn_char;
19
20 static void resize_map(int n);
21
22 #if 0 /* (should preserve in help somewhere) */
23 tbcat(buf, "To move a unit, use [hjklyubn]\n");
24 tbcat(buf, "[HJKLYUBN] moves unit repeatedly in that direction\n");
25 tbcat(buf, "To look at another unit, use survey mode ('z')\n");
26 tbcat(buf, "and use [hjklyubnHJKLYUBN] to move the cursor\n");
27 #endif
28
29 /* Use this macro in any command if it requires a current unit. */
30
31 #define REQUIRE_UNIT() \
32 if (!in_play(curunit)) { \
33 curunit = NULL; \
34 cmd_error(side, "No current unit to command!"); \
35 return; \
36 }
37
38 Unit *lastactor = NULL;
39
40 Unit *
find_next_and_look()41 find_next_and_look()
42 {
43 Unit *nextunit;
44
45 nextunit = find_next_actor(dside, curunit);
46 if (nextunit != NULL) {
47 make_current(nextunit);
48 show_cursor();
49 }
50 return nextunit;
51 }
52
53 static UnitVector *selvec;
54
55 UnitVector *
get_selected_units(Side * side)56 get_selected_units(Side *side)
57 {
58 if (selvec == NULL)
59 selvec = make_unit_vector(2);
60 clear_unit_vector(selvec);
61 if (in_play(curunit))
62 selvec = add_unit_to_vector(selvec, curunit, 0);
63 return selvec;
64 }
65
66 void
do_add_terrain(Side * side)67 do_add_terrain(Side *side)
68 {
69 int u, t, dir;
70
71 REQUIRE_UNIT();
72 u = curunit->type;
73 if (ask_direction("Add terrain to where?", &dir)) {
74 for_all_terrain_types(t) {
75 if (ut_acp_to_add_terrain(u, t) > 0
76 && curunit->act
77 && curunit->act->acp >= ut_acp_to_add_terrain(u, t)) {
78 if (0 <= ut_alter_range(curunit->type, t)) {
79 if (net_prep_add_terrain_action(curunit, curunit,
80 curunit->x, curunit->y,
81 dir, t))
82 ;
83 else
84 xbeep();
85 }
86 }
87 }
88 }
89 }
90
91 void
do_attack(Side * side)92 do_attack(Side *side)
93 {
94 int x, y;
95 Unit *other;
96
97 REQUIRE_UNIT();
98 if (ask_position("Attack where?", &x, &y)) {
99 for_all_stack(x, y, other) {
100 if (!unit_trusts_unit(curunit, other)) {
101 if (valid(check_attack_action(curunit, curunit, other, 100))) {
102 net_prep_attack_action(curunit, curunit, other, 100);
103 return;
104 }
105 /* (should try other types of actions?) */
106 }
107 }
108 cmd_error(side, "Nothing for %s to attack at %d,%d!",
109 unit_handle(dside, curunit), x, y);
110 }
111 }
112
113 void
do_build(Side * side)114 do_build(Side *side)
115 {
116 int u, u2, numtypes, ufirst;
117
118 REQUIRE_UNIT();
119 u = curunit->type;
120 if (!can_build(curunit)) {
121 cmd_error(side, "%s cannot build anything!", unit_handle(dside, curunit));
122 return;
123 }
124 numtypes = 0;
125 for_all_unit_types(u2) {
126 if (unit_can_build_type(curunit, u2)) {
127 tmp_u_array[u2] = TRUE;
128 ++numtypes;
129 ufirst = u2;
130 } else {
131 tmp_u_array[u2] = FALSE;
132 }
133 }
134 if (curunit->transport != NULL
135 && !uu_occ_can_build(curunit->transport->type, u)
136 && !(!completed(curunit->transport)
137 && uu_acp_to_build(u, curunit->transport->type) > 0)) {
138 cmd_error(side, "%s cannot build while inside another unit!",
139 unit_handle(side, curunit));
140 return;
141 }
142 switch (numtypes) {
143 case 0:
144 cmd_error(side, "%s cannot build anything right now!",
145 unit_handle(side, curunit));
146 break;
147 case 1:
148 /* Only one type to build - just do it. */
149 if (valid(can_create_in(curunit, curunit, ufirst, curunit)))
150 impl_build(side, curunit, ufirst, curunit, -1, -1, prefixarg);
151 else
152 impl_build(
153 side, curunit, ufirst, NULL, curunit->x, curunit->y, prefixarg);
154 break;
155 default:
156 /* Player has to choose a type to build. */
157 u2 = ask_unit_type("Type to build:", tmp_u_array);
158 if (u2 != NONUTYPE) {
159 if (valid(can_create_in(curunit, curunit, ufirst, curunit)))
160 impl_build(side, curunit, u2, curunit, -1, -1, prefixarg);
161 else
162 impl_build(
163 side, curunit, ufirst, NULL, curunit->x, curunit->y,
164 prefixarg);
165 } else {
166 /* should clear toplines */
167 }
168 break;
169 }
170 }
171
172 void
do_change_type(Side * side)173 do_change_type(Side *side)
174 {
175 int u = NONUTYPE, u2 = NONUTYPE, numtypes = 0, ufirst = NONUTYPE;
176
177 REQUIRE_UNIT();
178 u = curunit->type;
179 if (curunit->side != side) {
180 cmd_error(side, "%s is not your unit!", unit_handle(side, curunit));
181 return;
182 }
183 for_all_unit_types(u2) {
184 if (uu_acp_to_change_type(curunit->type, u2)) {
185 tmp_u_array[u2] = TRUE;
186 ++numtypes;
187 ufirst = u2;
188 }
189 else {
190 tmp_u_array[u2] = FALSE;
191 }
192 }
193 switch (numtypes) {
194 case 0:
195 cmd_error(side, "Cannot change to any type right now!");
196 break;
197 case 1:
198 /* Only one possible type to change to. Change to it. */
199 impl_change_type(side, curunit, ufirst);
200 break;
201 default:
202 u2 = ask_unit_type("Type to change into:", tmp_u_array);
203 if (u2 != NONUTYPE) {
204 impl_change_type(side, curunit, ufirst);
205 }
206 else {
207 /* should clear toplines */
208 }
209 break;
210 }
211 }
212
213 void
do_collect(Side * side)214 do_collect(Side *side)
215 {
216 int mtocollect;
217 char *arg, *rest;
218
219 REQUIRE_UNIT();
220 mtocollect = NONMTYPE;
221 if (nummtypes == 0) {
222 cmd_error(side, "No materials to collect");
223 return;
224 }
225 if (!empty_string(cmdargstr)) {
226 rest = get_next_arg(cmdargstr, tmpbuf, &arg);
227 mtocollect = mtype_from_name(arg);
228 } else {
229 cmd_error(side, "No material name given");
230 return;
231 }
232 if (!is_material_type(mtocollect)) {
233 cmd_error(side, "`%s' is not a recognized material name", arg);
234 return;
235 }
236 if (curunit->plan)
237 net_set_collect_task(curunit, mtocollect, curunit->x, curunit->y);
238 }
239
240 void
do_copying(Side * side)241 do_copying(Side *side)
242 {
243 cur_help_node = copying_help_node;
244 do_help(side);
245 }
246
247 /* Supposedly you could only get to these by typing the full command names. */
248
249 void
do_dir(Side * side)250 do_dir(Side *side)
251 {
252 int ndirs, dir1, dir2, modif;
253
254 ndirs = char_to_dir(tmpkey, &dir1, &dir2, &modif);
255 if (ndirs >= 1) {
256 do_dir_2(dir1, prefixarg);
257 } else {
258 xbeep();
259 return;
260 }
261 }
262
263 void
do_dir_multiple(Side * side)264 do_dir_multiple(Side *side)
265 {
266 cmd_error(side, "use the single-character commands instead");
267 }
268
269 /* Determine how far away another point is. */
270
271 void
do_distance(Side * side)272 do_distance(Side *side)
273 {
274 int x, y;
275
276 if (ask_position("Distance to where?", &x, &y)) {
277 notify(dside, "Distance is %d cells.", distance(curx, cury, x, y));
278 }
279 }
280
281 void
do_escape(Side * side)282 do_escape(Side *side)
283 {
284 cmd_error(side, "No curses version of this command.");
285 }
286
287 /* Command to fire at a specified unit or location. */
288
289 void
do_fire(Side * side)290 do_fire(Side *side)
291 {
292 int x, y;
293 Unit *unit2;
294 UnitView *uview;
295
296 REQUIRE_UNIT();
297 sprintf(spbuf, "Fire %s at where?", unit_handle(dside, curunit));
298 /* (should have some sort of range feedback) */
299 if (ask_position(spbuf, &x, &y)) {
300 for_all_view_stack(dside, x, y, uview) {
301 unit2 = view_unit(uview);
302 /* (should avoid shooting at allies too) */
303 if (unit2 != NULL
304 && unit2->side != curunit->side
305 && uview->date == g_turn()) {
306 net_prep_fire_at_action(curunit, curunit, unit2, -1);
307 return;
308 }
309 }
310 notify(dside, "No target unit seen, firing blindly");
311 net_prep_fire_into_action(curunit, curunit, x, y, 0, -1);
312 }
313 }
314
315 void
do_fire_into(Side * side)316 do_fire_into(Side *side)
317 {
318 int x, y;
319
320 REQUIRE_UNIT();
321 sprintf(spbuf, "Fire %s at where?", unit_handle(dside, curunit));
322 /* (should have some sort of range feedback) */
323 if (ask_position(spbuf, &x, &y)) {
324 net_prep_fire_into_action(curunit, curunit, x, y, 0, -1);
325 }
326 }
327
328 void
do_flash(Side * side)329 do_flash(Side *side)
330 {
331 cmd_error(side, "No curses version of this command.");
332 }
333
334 /* Toggle the action following flag. */
335
336 void
do_follow_action(Side * side)337 do_follow_action(Side *side)
338 {
339 follow_action = !follow_action;
340 if (follow_action) {
341 notify(dside, "Following the action.");
342 } else {
343 notify(dside, "Not following the action.");
344 }
345 }
346
347 /* Give a unit to another side or "to" independence. */
348
349 void
do_give_unit(Side * side)350 do_give_unit(Side *side)
351 {
352 REQUIRE_UNIT();
353 #ifdef DESIGNERS
354 if (dside->designer) {
355 net_designer_change_side(curunit, side_n(prefixarg));
356 return;
357 }
358 #endif /* DESIGNERS */
359 if (1) { /* (should test both temporary and permanent invalidity) */
360 net_prep_change_side_action(curunit, curunit, side_n(prefixarg));
361 } else {
362 cmd_error(side, "You can't just give away the %s!", unit_handle(dside, curunit));
363 }
364 }
365
366 /* Bring up help info. */
367
368 void
do_help(Side * side)369 do_help(Side *side)
370 {
371 /* Switch to help mode, saving current mode first. */
372 prevmode = mode;
373 mode = HELP;
374 show_help();
375 refresh();
376 }
377
378 /* Set the display of various kinds of data. */
379
380 void
do_map(Side * side)381 do_map(Side *side)
382 {
383 int value;
384 char *str, *str2, tmpbuf[BUFSIZE];
385
386 if (cmdargstr) {
387 str = cmdargstr;
388 while (*str != '\0') {
389 /* Collect the next whitespace-separated token from the
390 arg string. */
391 while (*str != '\0' && *str == ' ')
392 ++str;
393 str2 = tmpbuf;
394 while (*str != '\0' && *str != ' ')
395 *str2++ = *str++;
396 *str2 = '\0';
397 str2 = tmpbuf;
398 value = TRUE;
399 /* See if it is prefixed with a "-" or "no". */
400 if (*str2 == '-') {
401 value = FALSE;
402 ++str2;
403 } else if (*str2 == 'n' && *(str2+1) == 'o') {
404 value = FALSE;
405 str2 += 2;
406 }
407 if (strcmp(str2, "terrain") == 0 || strcmp(str2, "t") == 0) {
408 drawterrain = value;
409 } else if (strcmp(str2, "unit") == 0 || strcmp(str2, "u") == 0) {
410 drawunits = value;
411 } else if (strcmp(str2, "name") == 0 || strcmp(str2, "n") == 0) {
412 drawnames = value;
413 } else if (strcmp(str2, "people") == 0 || strcmp(str2, "p") == 0) {
414 drawpeople = value;
415 } else if (strcmp(str2, "cover") == 0 || strcmp(str2, "c") == 0) {
416 draw_cover = value;
417 } else if (strcmp(str2, "one") == 0 || strcmp(str2, "1") == 0) {
418 use_both_chars = !value;
419 } else if (strcmp(str2, "two") == 0 || strcmp(str2, "2") == 0) {
420 use_both_chars = value;
421 } else if (strncmp(str2, "lin", 3) == 0) {
422 drawlinear = value;
423 if (value && str2[3] == '=' && str2[4] != '\0') {
424 linear_char = str2[4];
425 }
426 } else if (strcmp(str2, ">") == 0) {
427 if (prefixarg < 0)
428 prefixarg = 5;
429 if (lw <= 5 && prefixarg > 0) {
430 cmd_error(side, "list side must be at least 5");
431 return;
432 }
433 if (lw - prefixarg < 5)
434 prefixarg = lw - 5;
435 resize_map(prefixarg);
436 return;
437 } else if (strcmp(str2, "<") == 0) {
438 if (prefixarg < 0)
439 prefixarg = 5;
440 if (mw <= 10 && prefixarg > 0) {
441 cmd_error(side, "map side must be at least 10");
442 return;
443 }
444 if (mw - prefixarg < 10)
445 prefixarg = mw - 10;
446 resize_map(0 - prefixarg);
447 return;
448 } else if (strcmp(str2, "_") == 0) {
449 if (prefixarg < 0)
450 prefixarg = 5;
451 if (prefixarg < 1)
452 prefixarg = 1;
453 if (prefixarg > 10)
454 prefixarg = 10;
455 infoh = prefixarg;
456 mh = LINES - 2 - infoh - 1;
457 closeupwin->h = infoh;
458 mapwin->y = 2 + infoh;
459 mapwin->h = mh + 1;
460 /* Update the screen to reflect the changes. */
461 set_scroll();
462 redraw();
463 } else if (strcmp(str2, "v") == 0) {
464 if (prefixarg < 0) {
465 cycle_list_type();
466 } else if (prefixarg == 0) {
467 cycle_list_filter();
468 } else if (prefixarg == 1) {
469 cycle_list_order();
470 }
471 show_list();
472 refresh();
473 } else {
474 cmd_error(side, "\"%s\" not recognized", tmpbuf);
475 }
476 }
477 } else {
478 notify(dside, "Nothing to do.");
479 }
480 show_map();
481 refresh();
482 }
483
484 static void
resize_map(int n)485 resize_map(int n)
486 {
487 /* Resize the left-hand-side windows. */
488 mw += n;
489 closeupwin->w += n;
490 mapwin->w += n;
491 /* Move and resize the right-hand-side windows. */
492 lw -= n;
493 sideswin->x += n;
494 sideswin->w -= n;
495 listwin->x += n;
496 listwin->w -= n;
497 /* Update the screen to reflect the changes. */
498 set_scroll();
499 redraw();
500 }
501
502 /* Send a short message to another side. */
503
504 void
do_message(Side * side)505 do_message(Side *side)
506 {
507 char *msg;
508 Side *side2;
509 SideMask sidemask;
510
511 if (prefixarg == 0) {
512 /* (should ask who to send to) */
513 }
514 side2 = side_n(prefixarg);
515 if (ask_string("Message:", "", &msg)) {
516 if (empty_string(msg) || (prefixarg >= 0 && side2 == NULL)) {
517 notify(dside, "You keep your mouth shut.");
518 sidemask = NOSIDES;
519 } else if (prefixarg < 0) {
520 notify(dside, "You made the announcement \"%s\"", msg);
521 sidemask = ALLSIDES;
522 } else if (side2 != NULL) {
523 notify(dside, "Your message was sent.");
524 sidemask = add_side_to_set(side2, NOSIDES);
525 }
526 if (!empty_string(msg) && sidemask != NOSIDES)
527 net_send_message(dside, sidemask, msg);
528 }
529 }
530
531 /* Set unit to move to a given location. Designers do a teleport. */
532
533 void
do_move_to(Side * side)534 do_move_to(Side *side)
535 {
536 int x, y;
537
538 REQUIRE_UNIT();
539 if (!is_designer(side)) {
540 if (!mobile(curunit->type)) {
541 cmd_error(side, "%s cannot move at all!", unit_handle(side, curunit));
542 return;
543 }
544 }
545 sprintf(spbuf, "Move %s to where?", unit_handle(dside, curunit));
546 if (ask_position(spbuf, &x, &y)) {
547 if (impl_move_to(side, curunit, x, y, 0)) {
548 return;
549 } else {
550
551 }
552 }
553 }
554
555 /* Command to name or rename the current unit or a given side. */
556
557 void
do_name(Side * side)558 do_name(Side *side)
559 {
560 char *newname;
561
562 REQUIRE_UNIT();
563 if (ask_string("New name for unit:", curunit->name, &newname)) {
564 if (empty_string(newname))
565 newname = NULL;
566 net_set_unit_name(dside, curunit, newname);
567 }
568 }
569
570 void
do_new_map(Side * side)571 do_new_map(Side *side)
572 {
573 cmd_error(side, "No curses version of this command.");
574 }
575
576 void
do_occupant(Side * side)577 do_occupant(Side *side)
578 {
579 Unit *nextocc;
580
581 if (curunit == NULL) {
582 make_current_at(curx, cury);
583 }
584 REQUIRE_UNIT();
585 nextocc = find_next_occupant(curunit);
586 if (nextocc != curunit)
587 make_current(nextocc);
588 }
589
590 void
do_orders_popup(Side * side)591 do_orders_popup(Side *side)
592 {
593 cmd_error(side, "No curses version of this command.");
594 }
595
596 void
do_other(Side * side)597 do_other(Side *side)
598 {
599 char *cmd;
600
601 if (ask_string("Command:", NULL, &cmd)) {
602 if (empty_string(cmd)) {
603 cmd_error(side, "No command");
604 } else if (strcmp(cmd, "?") == 0) {
605 cur_help_node = long_commands_help_node;
606 do_help(side);
607 } else {
608 execute_long_command(side, cmd);
609 }
610 }
611 }
612
613 void
do_print_view(Side * side)614 do_print_view(Side *side)
615 {
616 dump_text_view(dside, use_both_chars);
617 }
618
619 void
do_produce(Side * side)620 do_produce(Side *side)
621 {
622 int m, n;
623 Unit *unit = curunit;
624
625 REQUIRE_UNIT();
626 if (!can_produce(unit)) {
627 cmd_error(side, "cannot do active production");
628 }
629 n = 9999;
630 if (prefixarg > 0)
631 n = prefixarg;
632 /* Find the first produceable type and set up to produce it. */
633 for_all_material_types(m) {
634 if (um_acp_to_produce(unit->type, m) > 0) {
635 net_push_produce_task(unit, m, n);
636 return;
637 }
638 }
639 }
640
641 /* Command to get out of a game, one way or another. */
642
643 void
do_quit(Side * side)644 do_quit(Side *side)
645 {
646 if (endofgame || beforestart || !dside->ingame) {
647 exit_cconq();
648 return;
649 }
650 /* Confirm the saving of any state. */
651 if (!gamestatesafe) {
652 if (ask_bool("Do you want to save the game?", TRUE)) {
653 if (all_others_willing_to_save(dside)) {
654 do_save(side);
655 exit_cconq();
656 return;
657 } else {
658 net_set_willing_to_save(dside, TRUE);
659 notify(dside, "Other sides not willing to save.");
660 }
661 }
662 }
663 if (all_others_willing_to_quit(dside)) {
664 if (ask_bool("Do you really want to declare a draw?", FALSE)) {
665 net_set_willing_to_draw(dside, TRUE);
666 } else {
667 notify(dside, "Not willing to draw.");
668 }
669 return;
670 } else {
671 if (ask_bool("You must resign to get out; do you want to resign?", FALSE)) {
672 do_resign(side);
673 } else {
674 notify(dside, "Not resigning.");
675 }
676 }
677 }
678
679 /* Move the current location as close to the center of the display as
680 possible, and redraw everything. */
681
682 void
do_recenter(Side * side)683 do_recenter(Side *side)
684 {
685 set_view_focus(mvp, curx, cury);
686 center_on_focus(mvp);
687 set_map_viewport();
688 show_map();
689 refresh();
690 }
691
692 /* Redraw everything using the same code as when windows need a redraw. */
693
694 void
do_refresh(Side * side)695 do_refresh(Side *side)
696 {
697 redraw();
698 }
699
700 void
do_remove_terrain(Side * side)701 do_remove_terrain(Side *side)
702 {
703 int t, dir;
704
705 REQUIRE_UNIT();
706 if (ask_direction("Remove terrain from where?", &dir)) {
707 for_all_terrain_types(t) {
708 if (ut_acp_to_remove_terrain(curunit->type, t) > 0
709 && curunit->act
710 && curunit->act->acp >= ut_acp_to_remove_terrain(curunit->type, t)) {
711 if (0 <= ut_alter_range(curunit->type, t)) {
712 if (net_prep_remove_terrain_action(curunit, curunit, curunit->x, curunit->y, dir, t))
713 ;
714 else
715 xbeep();
716 }
717 }
718 }
719 }
720 }
721
722 void
do_repair(Side * side)723 do_repair(Side *side)
724 {
725 cmd_error(side, "repair command not implemented");
726 }
727
728 void
do_resign(Side * side)729 do_resign(Side *side)
730 {
731 Side *side2;
732
733 if (endofgame) {
734 cmd_error(side, "Game is already over.");
735 } else if (!dside->ingame) {
736 cmd_error(side, "You are already out of the game.");
737 } else if (ask_bool("Do you really want to resign?", FALSE)) {
738 side2 = NULL;
739 if (numsides > 2) {
740 side2 = ask_side("Who do you want to inherit?", NULL);
741 if (side2 == dside) {
742 cmd_error(side, "You can't inherit your own units! (not giving to anybody)");
743 side2 = NULL;
744 }
745 }
746 net_resign_game(dside, side2);
747 }
748 }
749
750 /* Stuff game state into a file. By default, it goes into the current
751 directory. If building a scenario, we can specify just which parts
752 of the game state are to be written. */
753
754 void
do_save(Side * side)755 do_save(Side *side)
756 {
757 char *rawcontents;
758 Module *module;
759 Obj *contents;
760
761 #ifdef DESIGNERS
762 if (dside->designer) {
763 if (ask_string("Data to write?", "everything", &rawcontents)) {
764 /* (should be in a designer_create_module?) */
765 /* need to be able to get this name from somewhere */
766 module = create_game_module("random.scn");
767 /* need something better to turn contents into a Lisp object */
768 contents = intern_symbol(rawcontents);
769 /* interpret_content_spec(module, contents); */
770 notify(dside, "Module will be written to \"%s\" ...", module->filename);
771 /* This seems broken anyway, but we add module->filename to
772 make the code compile ... */
773 if (write_game_module(module, module->filename)) {
774 notify(dside, "Done writing to \"%s\".", module->filename);
775 } else {
776 cmd_error(side, "Can't open file \"%s\"!", module->filename);
777 }
778 return;
779 } else {
780 return;
781 }
782 }
783 #endif /* DESIGNERS */
784 if (0 /* checkpointing not allowed */) {
785 if (ask_bool("You really want to save and exit?", FALSE)) {
786 notify(dside, "Game will be saved to \"%s\" ...", saved_game_filename());
787 if (write_entire_game_state(saved_game_filename())) {
788 close_displays();
789 /* this should be conditional? */
790 exit(0);
791 } else {
792 cmd_error(side, "Can't open file \"%s\"!", saved_game_filename());
793 }
794 }
795 } else {
796 notify(dside, "Saving...");
797 if (write_entire_game_state(saved_game_filename())) {
798 notify(dside, "Game saved.");
799 } else {
800 cmd_error(side, "Couldn't save to \"%s\"!", saved_game_filename());
801 }
802 }
803 }
804
805 void
do_set_formation(Side * side)806 do_set_formation(Side *side)
807 {
808 Unit *leader;
809
810 REQUIRE_UNIT();
811 sprintf(spbuf, "Which unit to follow?");
812 if (ask_unit(spbuf, &leader)) {
813 if (!in_play(leader)) {
814 cmd_error(side, "No unit to follow!");
815 } else if (leader == curunit) {
816 cmd_error(side, "Unit can't follow itself!");
817 } else if (leader->side != dside /* or "trusted side"? */) {
818 cmd_error(side, "Can't follow somebody else's unit!");
819 } else {
820 net_set_formation(curunit, leader, curunit->x - leader->x, curunit->y - leader->y, 1, 1);
821 }
822 }
823 }
824
825 void
do_set_view_angle(Side * side)826 do_set_view_angle(Side *side)
827 {
828 cmd_error(side, "No curses version of this command.");
829 }
830
831 void
do_side_closeup(Side * side)832 do_side_closeup(Side *side)
833 {
834 cmd_error(side, "No curses version of this command.");
835 }
836
837 void
do_standing_orders(Side * side)838 do_standing_orders(Side *side)
839 {
840 int rslt;
841
842 if (cmdargstr) {
843 rslt = parse_standing_order(dside, cmdargstr);
844 if (rslt < 0)
845 xbeep();
846 } else
847 xbeep();
848 }
849
850 /* Command to toggle between interaction modes. */
851
852 void
do_survey(Side * side)853 do_survey(Side *side)
854 {
855 if (mode == MOVE) {
856 lastactor = curunit;
857 mode = SURVEY;
858 } else {
859 mode = MOVE;
860 /* If we weren't looking at a unit when we switched modes,
861 go back to the last unit that was being moved. */
862 if (curunit == NULL && in_play(lastactor)) {
863 make_current(lastactor);
864 }
865 }
866 show_map();
867 refresh();
868 }
869
870 void
do_unit_closeup(Side * side)871 do_unit_closeup(Side *side)
872 {
873 cmd_error(side, "No curses version of this command.");
874 }
875
876 void
do_up(Side * side)877 do_up(Side *side)
878 {
879 cmd_error(side, "No curses version of this command.");
880 }
881
882 /* Display the program version. */
883
884 void
do_version(Side * side)885 do_version(Side *side)
886 {
887 notify(dside, "Curses Xconq version %s", version_string());
888 notify(dside, "(c) %s", copyright_string());
889 }
890
891 void
do_warranty(Side * side)892 do_warranty(Side *side)
893 {
894 cur_help_node = warranty_help_node;
895 do_help(side);
896 }
897
898 void
do_world_map(Side * side)899 do_world_map(Side *side)
900 {
901 cmd_error(side, "No curses version of this command.");
902 }
903
904 void
do_zoom_in(Side * side)905 do_zoom_in(Side *side)
906 {
907 cmd_error(side, "No curses version of this command.");
908 }
909
910 void
do_zoom_out(Side * side)911 do_zoom_out(Side *side)
912 {
913 cmd_error(side, "No curses version of this command.");
914 }
915
916 #ifdef DESIGNERS
917
918 void
do_design(Side * side)919 do_design(Side *side)
920 {
921 if (!dside->designer) {
922 net_become_designer(dside);
923 } else {
924 net_become_nondesigner(dside);
925 }
926 }
927
928 #endif /* DESIGNERS */
929
930 #ifdef DEBUGGING
931
932 void
do_profile(Side * side)933 do_profile(Side *side)
934 {
935 cmd_error(side, "No curses version of this command.");
936 }
937
938 void
do_trace(Side * side)939 do_trace(Side *side)
940 {
941 cmd_error(side, "No curses version of this command.");
942 }
943
944 #endif /* DEBUGGING */
945
946 /* Generic command error routine just does a notify. */
947
948 void
cmd_error(Side * side,char * fmt,...)949 cmd_error(Side *side, char *fmt, ...)
950 {
951 char tmpnbuf[BUFSIZE];
952 va_list ap;
953
954 if (!empty_string(fmt)) {
955
956 va_start(ap, fmt);
957 vsnprintf(tmpnbuf, sizeof tmpnbuf, fmt, ap);
958 va_end(ap);
959
960 low_notify(dside, tmpnbuf);
961 }
962 xbeep();
963 }
964