1 /* Generic commands.
2    Copyright (C) 1998-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 /* The commands in this file are the same for all interfaces.  They
10    typically consist of direct operations on units, or complicated
11    command-line-type commands. */
12 
13 #include "conq.h"
14 #include "kpublic.h"
15 #include "imf.h"
16 #include "ui.h"
17 
18 int autofinish_start;
19 int autofinish_count;
20 
21 static void notify_relationships(Side *side);
22 static int parse_advance_spec(char *str);
23 static int do_one_auto(Side *side, Unit *unit);
24 static int do_one_clear_plan(Side *side, Unit *unit);
25 static int do_one_delay(Side *side, Unit *unit);
26 static int do_one_detach(Side *side, Unit *unit);
27 static int do_one_detonate(Side *side, Unit *unit);
28 static int do_one_disband(Side *side, Unit *unit);
29 static int do_one_disembark(Side *side, Unit *unit);
30 static int do_one_embark(Side *side, Unit *unit);
31 static int do_one_give(Side *side, Unit *unit);
32 static int do_one_idle(Side *side, Unit *unit);
33 static int do_one_reserve(Side *side, Unit *unit);
34 static int do_one_return(Side *side, Unit *unit);
35 static int do_one_sleep(Side *side, Unit *unit);
36 static int do_one_take(Side *side, Unit *unit);
37 static int do_one_wake(Side *side, Unit *unit);
38 static int do_one_wake_all(Side *side, Unit *unit);
39 
40 /* The command table is an array of all the commands. */
41 
42 typedef struct cmdtab {
43     char fchar;			/* character to match against */
44     char *name;			/* Full name of command */
45     void (*fn)(Side *side);	/* Pointer to command's function */
46     char *help;                 /* short documentation string */
47 } CmdTab;
48 
49 #define C(c) ((c)-0x40)
50 
51 #undef DEF_CMD
52 #define DEF_CMD(LETTER,NAME,FN,HELP) { LETTER, NAME, FN, HELP },
53 
54 /* Define the table of commands. */
55 
56 CmdTab commands[] = {
57 
58 #include "cmd.def"
59 
60   { 0, NULL, /* NULL, */ NULL, NULL }
61 };
62 
63 /* This is the actual key typed, for use if several keyboard commands
64    share a single function. */
65 
66 char tmpkey;
67 
68 /* If the command was typed as a command-line sort of thing, then this
69    global will point to whatever came after the name of the
70    command. */
71 
72 char *cmdargstr;
73 
74 /* Help nodes that list all the commands, both single-char and long. */
75 
76 HelpNode *key_commands_help_node;
77 
78 HelpNode *long_commands_help_node;
79 
80 HelpNode *map_help_node;
81 
82 /* Search in command table and execute function if found, complaining if
83    the command is not recognized. */
84 
85 void
execute_command(Side * side,int ch)86 execute_command(Side *side, int ch)
87 {
88     CmdTab *cmd;
89     void (*fn)(Side *side);
90 
91     /* The long-name commands all have a char of 0, so we must ensure
92        nonzero characters coming in here. */
93     if (ch == '\0')
94       run_error("Character '\\0' in execute_command");
95     cmdargstr = NULL;
96     for (cmd = commands; cmd->name != NULL; ++cmd) {
97 	if (ch == cmd->fchar) {
98 	    fn = cmd->fn;
99 	    if (fn == NULL) {
100 		run_warning("no command function for %s (0x%x)?",
101 			    cmd->name, ch);
102 		return;
103 	    }
104 	    tmpkey = ch;
105 	    (*fn)(side);
106 	    /* Whatever might have happened, we *did* find the command. */
107 	    return;
108 	}
109     }
110     cmd_error(side, "unrecognized command key '%c'", ch);
111 }
112 
113 /* Given a string, find and interpret a long-name command in it. */
114 
115 void
execute_long_command(Side * side,char * cmdstr)116 execute_long_command(Side *side, char *cmdstr)
117 {
118     int prefix;
119     char *cmdname;
120     CmdTab *cmd;
121     void (*fn)(Side *side);
122 
123     /* (should do something with prefix, which is presently ignored) */
124     parse_long_name_command(cmdstr, &prefix, &cmdname, &cmdargstr,
125 			    copy_string(cmdstr));
126     if (empty_string(cmdname)) {
127 	/* Treat this is a deliberate cancellation, not an error. */
128 	notify(side, "No command.");
129 	return;
130     }
131     for (cmd = commands; cmd->name != NULL; ++cmd) {
132 	if (strcmp(cmdname, cmd->name) == 0) {
133 	    fn = cmd->fn;
134 	    if (fn == NULL) {
135 		run_warning("no command function for %s?", cmd->name);
136 		return;
137 	    }
138 	    tmpkey = cmd->fchar;
139 	    (*fn)(side);
140 	    /* Whatever might have happened, we *did* find the command. */
141 	    return;
142 	}
143     }
144     cmd_error(side, "unrecognized command name \"%s\"", cmdname);
145 }
146 
147 /* Collect a command name and an argument string from the given
148    string, discarding excess whitespace. */
149 
150 void
parse_long_name_command(char * cmdstr,int * prefixp,char ** namep,char ** argp,char * buf)151 parse_long_name_command(char *cmdstr, int *prefixp, char **namep, char **argp,
152 			char *buf)
153 {
154     int j, prefixarg = -1;
155     char *cmdname, *cmdarg;
156 
157     if (empty_string(cmdstr)) {
158 	*namep = *argp = NULL;
159 	return;
160     }
161     strcpy(buf, cmdstr);
162     /* Look for the first nonwhite char, make it start of command name. */
163     cmdname = buf;
164     while ((*cmdname == ' ' || *cmdname == '\t') && *cmdname != '\0')
165       ++cmdname;
166     /* If this is a digit, then it's a prefix arg; extract it. */
167     if (isdigit(*cmdname)) {
168 	prefixarg = *cmdname - '0';
169 	++cmdname;
170 	while (isdigit(*cmdname)) {
171 	    prefixarg = prefixarg * 10 + (*cmdname - '0');
172 	    ++cmdname;
173 	}
174 	/* Skip over any space between prefix arg and cmdname. */
175 	while ((*cmdname == ' ' || *cmdname == '\t') && *cmdname != '\0')
176 	  ++cmdname;
177     }
178     /* Scan over command name, which is delimited by whitespace or end
179        of line. */
180     for (j = 0; cmdname[j] != ' ' && cmdname[j] != '\t' && cmdname[j] != '\n' && cmdname[j] != '\0'; ++j)
181       ;
182     /* If there's more than just the command name, extract an argument. */
183     if (cmdname[j] != '\0') {
184 	cmdarg = cmdname + j + 1;
185 	while ((*cmdarg == ' ' || *cmdname == '\t' || *cmdname == '\n') && *cmdarg != '\0')
186 	  ++cmdarg;
187 	if (*cmdarg == '\0')
188 	  cmdarg = NULL;
189     } else {
190 	cmdarg = NULL;
191     }
192     /* Terminate the command name. */
193     cmdname[j] = '\0';
194     /* Remove trailing whitespace from the argument. */
195     if (!empty_string(cmdarg)) {
196 	for (j = strlen(cmdarg) - 1; j >= 0 && (cmdarg[j] == ' ' || cmdarg[j] == '\t' || cmdarg[j] == '\n'); --j)
197 	  ;
198 	cmdarg[j + 1] = '\0';
199     }
200     DGprintf("Command is \"%s\", argument is \"%s\", prefix is %d\n",
201 	     (cmdname != NULL ? cmdname : "<null>"),
202 	     (cmdarg != NULL ? cmdarg : "<null>"),
203 	     prefixarg);
204     *prefixp = prefixarg;
205     *namep = cmdname;
206     *argp = cmdarg;
207 }
208 
209 /* Describe all the single-key commands for help. */
210 
211 void
describe_key_commands(int arg,char * key,TextBuffer * buf)212 describe_key_commands(int arg, char *key, TextBuffer *buf)
213 {
214     CmdTab *cmd;
215 
216     for (cmd = commands; cmd->name != NULL; ++cmd) {
217 	describe_command(cmd->fchar, cmd->name, cmd->help, TRUE, buf);
218     }
219 }
220 
221 /* Describe all the long-name commands for help. */
222 
223 void
describe_long_commands(int arg,char * key,TextBuffer * buf)224 describe_long_commands(int arg, char *key, TextBuffer *buf)
225 {
226     CmdTab *cmd;
227 
228     for (cmd = commands; cmd->name != NULL; ++cmd) {
229 	describe_command (cmd->fchar, cmd->name, cmd->help, FALSE, buf);
230     }
231 }
232 
233 static int
apply_to_all_selected_units(Side * side,int (* fn)(Side * side,Unit * unit))234 apply_to_all_selected_units(Side *side, int (*fn)(Side *side, Unit *unit))
235 {
236     int i, rslt, numcould = 0, numfail = 0;
237     Unit *unit;
238     UnitVector *selvec;
239 
240     if (fn == NULL)
241       /* (should be a fatal error?) */
242       return 0;
243     /* Scan all the selected units. Note that we assume that the list
244        (unit vector) doesn't change while we're scanning, which means
245        that we should be checking each unit, in case a command caused
246        later units in the list to disappear (for instance a
247        detonation). */
248     selvec = get_selected_units(side);
249     for (i = 0; i < selvec->numunits; ++i) {
250 	unit = unit_in_vector(selvec, i);
251 	if (in_play(unit) && side_controls_unit(side, unit)) {
252 	    ++numcould;
253 	    rslt = (*fn)(side, unit);
254 	    if (!rslt)
255 	      ++numfail;
256 	}
257     }
258     /* Return true if everything went OK, false if the function failed
259        on some units. */
260     return ((numcould > 0) ? (numfail == 0) : TRUE);
261 }
262 
263 /* Generic command functions.  These are the command functions that
264    behave identically for all interfaces. */
265 
266 #define DURING_GAME_ONLY(side)  \
267   if (endofgame) {  \
268     cmd_error((side), "Cannot do after game is over!");  \
269     return;  \
270   }
271 
272 void
do_add_player(Side * side)273 do_add_player(Side *side)
274 {
275     DURING_GAME_ONLY(side);
276     net_request_additional_side(cmdargstr);
277 }
278 
279 #if 0
280 
281 void
282 do_agreement_draft(Side *side)
283 {
284     char *title, titlebuf[20];
285     Agreement *ag;
286 
287     DURING_GAME_ONLY(side);
288     /* (should have a net_create_agreement) */
289     ag = create_agreement(0);
290     if (!empty_string(cmdargstr)) {
291 	title = cmdargstr;
292     } else {
293 	sprintf(titlebuf, "#%d", ag->id);
294 	title = copy_string(titlebuf);
295     }
296     ag->name = title;
297     /* The creating side is by definition one of the drafters. */
298     ag->drafters = add_side_to_set(side, ag->drafters);
299 }
300 
301 void
302 do_agreement_drafter(Side *side)
303 {
304     DURING_GAME_ONLY(side);
305 }
306 
307 void
308 do_agreement_propose(Side *side)
309 {
310     DURING_GAME_ONLY(side);
311 }
312 
313 void
314 do_agreement_proposer(Side *side)
315 {
316     DURING_GAME_ONLY(side);
317 }
318 
319 void
320 do_agreement_sign(Side *side)
321 {
322     DURING_GAME_ONLY(side);
323 }
324 
325 void
326 do_agreement_signer(Side *side)
327 {
328     DURING_GAME_ONLY(side);
329 }
330 
331 void
332 do_agreement_term(Side *side)
333 {
334     int agid, agterm;
335     char *arg, *rest, *term;
336     Agreement *ag;
337 
338     DURING_GAME_ONLY(side);
339     rest = get_next_arg(cmdargstr, tmpbuf, &arg);
340     if (empty_string(arg)) {
341 	cmd_error(side, "no agreement id");
342 	return;
343     }
344     agid = strtol(arg, NULL, 10);
345     agterm = 0;
346     if (isdigit(*rest)) {
347 	rest = get_next_arg(rest, tmpbuf, &arg);
348 	agterm = strtol(arg, NULL, 10);
349     }
350     term = copy_string(rest);
351     ag = find_agreement(agid);
352     if (ag == NULL) {
353 	cmd_error(side, "no agreement #%d", agid);
354 	return;
355     }
356     /* (should be kernel function) */
357     ag->terms = cons(new_string(term), ag->terms);
358 }
359 
360 #endif
361 
362 /* Set which AI is to run the side's play. */
363 
364 void
do_ai_side(Side * side)365 do_ai_side(Side *side)
366 {
367     char *arg, *rest, *aitypename = NULL;
368 
369     DURING_GAME_ONLY(side);
370     /* Look at the optional command argument, extract options and/or
371        AI type name from it. */
372     if (!empty_string(cmdargstr)) {
373 	rest = get_next_arg(cmdargstr, tmpbuf, &arg);
374 	if (strcmp(arg, "help") == 0
375 	    || strcmp(arg, "?") == 0) {
376 	    aitypename = next_ai_type_name(NULL);
377 	    while (aitypename != NULL) {
378 		notify(side, " %s - %s",
379 		       aitypename, ai_type_help(find_ai_type(aitypename)));
380 		aitypename = next_ai_type_name(aitypename);
381 	    }
382 	    return;
383 	} else if (strcmp(arg, "+") == 0) {
384 	    /* Note that this flag is used by local AIs only, so don't
385 	       need to synchronize kernels here. */
386 	    side->ai_may_resign = TRUE;
387 	    notify(side, "AI may decide to draw or resign.");
388 	    return;
389 	} else if (strcmp(arg, "-") == 0) {
390 	    side->ai_may_resign = FALSE;
391 	    notify(side, "AI may only recommend whether to draw or resign.");
392 	    return;
393 	}
394 	aitypename = arg;
395     }
396     if (empty_string(aitypename)) {
397     	/* Toggle AI between mplayer and NULL. */
398 	/* (should toggle between "preferred AI" for the side) */
399     	if (side->player->aitypename == NULL) {
400 	    aitypename = "mplayer";
401 	} else {
402 	    aitypename = NULL;
403     	}
404     } else if (strcmp(aitypename, "none") == 0) {
405 	/* Special case to handle command "AI None" in Side menu. */
406 	aitypename = NULL;
407     }
408     /* Feed back to user.  Note that we say "want to have" because we
409        haven't gotten feedback from remote programs yet. */
410     if (!empty_string(aitypename)) {
411 	notify(side, "Want to have AI %s running this side.",
412 	       aitypename);
413     } else {
414 	notify(side, "Want to have no AI running this side.");
415     }
416     net_set_side_ai(side, aitypename);
417 }
418 
419 void
do_auto(Side * side)420 do_auto(Side *side)
421 {
422     DURING_GAME_ONLY(side);
423     apply_to_all_selected_units(side, do_one_auto);
424 }
425 
426 static int
do_one_auto(Side * side,Unit * unit)427 do_one_auto(Side *side, Unit *unit)
428 {
429     int newval;
430 
431     if (unit->plan == NULL)
432       return FALSE;
433     if (side->prefixarg < 0)
434       newval = !unit->plan->aicontrol;
435     else if (side->prefixarg == 0)
436       newval = FALSE;
437     else
438       newval = TRUE;
439     net_set_unit_ai_control(side, unit, newval, FALSE);
440     net_force_replan(unit);
441     return TRUE;
442 }
443 
444 void
do_c_rate(Side * side)445 do_c_rate(Side *side)
446 {
447     int m = NONMTYPE, m1, m2, n, tot, pct;
448     char *str, *reststr;
449     char tbuf[BUFSIZE];
450 
451     DURING_GAME_ONLY(side);
452     /* For now, assume only one m. */
453     for_all_material_types(m1) {
454 	for_all_material_types(m2) {
455 	    if (mm_conversion(m1, m2) > 0) {
456 		m = m1;
457 		break;
458 	    }
459 	}
460     }
461     if (m == NONMTYPE)
462       return;
463     if (side->c_rates == NULL)
464       side->c_rates = (short *) xmalloc(nummtypes * sizeof(short));
465     if (!empty_string(cmdargstr)) {
466 	str = cmdargstr;
467 	for_all_material_types(m2) {
468 	    if (mm_conversion(m, m2) > 0) {
469 		n = strtol(str, &reststr, 10);
470 		str = reststr;
471 		/* (should use a net_ routine) */
472 		side->c_rates[m2] = n;
473 	    }
474 	}
475     }
476     tot = 0;
477     for_all_material_types(m2) {
478 	if (mm_conversion(m, m2) > 0) {
479 	    tot += side->c_rates[m2];
480 	}
481     }
482     tbuf[0] = '\0';
483     for_all_material_types(m2) {
484 	if (mm_conversion(m, m2) > 0) {
485 	    pct = (tot > 0 ? ((side->c_rates[m2] * 100) / tot) : 0);
486 	    sprintf(tbuf+strlen(tbuf), "  %s %d%%", m_type_name(m2), pct);
487 	}
488     }
489     notify(side, "%s conversion rates: %s", m_type_name(m), tbuf);
490 }
491 
492 void
do_clear_plan(Side * side)493 do_clear_plan(Side *side)
494 {
495     DURING_GAME_ONLY(side);
496     apply_to_all_selected_units(side, do_one_clear_plan);
497 }
498 
499 static int
do_one_clear_plan(Side * side,Unit * unit)500 do_one_clear_plan(Side *side, Unit *unit)
501 {
502     if (unit->plan == NULL)
503       return FALSE;
504     net_set_unit_plan_type(side, unit, PLAN_NONE);
505     return TRUE;
506 }
507 
508 void
do_delay(Side * side)509 do_delay(Side *side)
510 {
511     DURING_GAME_ONLY(side);
512     apply_to_all_selected_units(side, do_one_delay);
513 }
514 
515 static int
do_one_delay(Side * side,Unit * unit)516 do_one_delay(Side *side, Unit *unit)
517 {
518     if (unit->plan == NULL)
519       return FALSE;
520     net_delay_unit(unit, TRUE);
521     return TRUE;
522 #if 0 /* add this case from tkcmd.c? */
523 	unit = find_next_awake_mover(side, map->curunit);
524 	if (unit != map->curunit) {
525 	    set_current_unit(map, unit);
526 	} else {
527 	    cmd_error(side, "No next awake mover found.");
528 	}
529 #endif
530 }
531 
532 void
do_detach(Side * side)533 do_detach(Side *side)
534 {
535     DURING_GAME_ONLY(side);
536     apply_to_all_selected_units(side, do_one_detach);
537 }
538 
539 static int
do_one_detach(Side * side,Unit * unit)540 do_one_detach(Side *side, Unit *unit)
541 {
542     int rslt;
543 
544     if (!completed(unit)) {
545 	cmd_error(side, "%s is incomplete; cannot detach",
546 		  unit_handle(side, unit));
547 	return FALSE;
548     }
549     rslt = check_transfer_part_action(unit, unit, unit->hp / 2, NULL);
550     if (valid(rslt)) {
551 	net_prep_transfer_part_action(unit, unit, unit->hp / 2, NULL);
552     } else {
553 	notify(side, "can't detach for some reason?");
554     }
555     return TRUE;
556 }
557 
558 void
do_detonate(Side * side)559 do_detonate(Side *side)
560 {
561     DURING_GAME_ONLY(side);
562     /* (should add Mac version's choice of where to detonate?) */
563     apply_to_all_selected_units(side, do_one_detonate);
564 }
565 
566 static int
do_one_detonate(Side * side,Unit * unit)567 do_one_detonate(Side *side, Unit *unit)
568 {
569     int rslt;
570 
571     rslt = check_detonate_action(unit, unit, unit->x, unit->y, unit->z);
572     if (valid(rslt)) {
573 	net_prep_detonate_action(unit, unit, unit->x, unit->y, unit->z);
574 	return TRUE;
575     } else {
576 	notify(side, "can't detonate for some reason?");
577 	return FALSE;
578     }
579 }
580 
581 /* Get rid of a unit. */
582 
583 void
do_disband(Side * side)584 do_disband(Side *side)
585 {
586     DURING_GAME_ONLY(side);
587     apply_to_all_selected_units(side, do_one_disband);
588 }
589 
590 static int
do_one_disband(Side * side,Unit * unit)591 do_one_disband(Side *side, Unit *unit)
592 {
593     /* (should have more feedback?) */
594     net_disband_unit(side, unit);
595     return TRUE;
596 }
597 
598 void
do_disembark(Side * side)599 do_disembark(Side *side)
600 {
601     DURING_GAME_ONLY(side);
602     apply_to_all_selected_units(side, do_one_disembark);
603 }
604 
605 static int
do_one_disembark(Side * side,Unit * unit)606 do_one_disembark(Side *side, Unit *unit)
607 {
608     Unit *transport;
609 
610     transport = unit->transport;
611     if (transport == NULL) {
612 	cmd_error(side, "Not in a transport");
613 	return FALSE;
614     }
615     if (!in_play(transport)) {
616 	cmd_error(side, "Transport is nonsensical?");
617 	return FALSE;
618     }
619     /* Try moving into the transport's transport, if there is one. */
620     if (transport->transport != NULL
621         && can_occupy(unit, transport->transport)) {
622 	net_prep_enter_action(unit, unit, transport->transport);
623 	/* (should be able to set up task if can't do action immediately) */
624 	return TRUE;
625     }
626     /* Try moving into the open in the cell. We don't test for
627     type_survives_in_cell here, so it is possible to deliberately
628     jump overboard. */
629     if (type_can_occupy_cell(unit->type, unit->x, unit->y)) {
630 	net_prep_move_action(unit, unit, unit->x, unit->y, unit->z);
631 	/* (should be able to set up task if can't do action immediately) */
632 	return TRUE;
633     }
634     cmd_error(side, "Can't disembark here!");
635     return FALSE;
636 }
637 
638 void
do_distrust(Side * side)639 do_distrust(Side *side)
640 {
641     Side *side2;
642 
643     DURING_GAME_ONLY(side);
644     if (empty_string(cmdargstr)) {
645 	notify_relationships(side);
646 	return;
647     }
648     side2 = parse_side_spec(cmdargstr);
649     if (side2 == NULL) {
650 	cmd_error(side, "No side matching \"%s\"", cmdargstr);
651 	return;
652     }
653     if (side2 == side) {
654 	cmd_error(side, "We're not confused about trusting ourselves!");
655 	return;
656     }
657     net_set_trust(side, side2, 0);
658 }
659 
660 void
notify_relationships(Side * side)661 notify_relationships(Side *side)
662 {
663     char *buf, buf1[1000], buf2[1000], buf3[1000], buf4[1000];
664     Side *side2;
665 
666     /* List the status of all other sides wrt us. */
667     buf1[0] = buf2[0] = buf3[0] = buf4[0] = '\0';
668     for_all_sides(side2) {
669 	if (side2 != side) {
670 	    /* Choose which of four possible buckets the side falls into. */
671 	    if (trusted_side(side, side2)) {
672 		if (trusted_side(side2, side)) {
673 		    buf = buf1;
674 		} else {
675 		    buf = buf2;
676 		}
677 	    } else {
678 		if (trusted_side(side2, side)) {
679 		    buf = buf3;
680 		} else {
681 		    buf = buf4;
682 		}
683 	    }
684 	    /* Add the side's title to the chosen bucket. */
685 	    if (strlen(buf) > 0)
686 	      strcat(buf, ", ");
687 	    strcat(buf, short_side_title(side2));
688 	}
689     }
690     /* Describe each bucket. */
691     if (strlen(buf1) > 0)
692       notify(side, "We trust them, and vice versa: %s.", buf1);
693     if (strlen(buf2) > 0)
694       notify(side, "We trust them, but they don't trust us: %s.", buf2);
695     if (strlen(buf3) > 0)
696       notify(side, "They trust us, but we don't trust them: %s.", buf3);
697     if (strlen(buf4) > 0)
698       notify(side, "We don't trust them, and vice versa: %s.", buf4);
699 }
700 
701 void
do_doctrine(Side * side)702 do_doctrine(Side *side)
703 {
704     DURING_GAME_ONLY(side);
705     if (!empty_string(cmdargstr)) {
706 	if (strcmp(cmdargstr, "set") == 0) {
707 	    notify(side, "Set what?");
708 	} else if (strncmp(cmdargstr, "set ", 4) == 0) {
709 	    net_set_doctrine(side, cmdargstr + 4);
710 	} else if (strcmp(cmdargstr, "help") == 0
711 		   || strcmp(cmdargstr, "?") == 0) {
712 	    notify(side, "doctrine set <doct-name> <property> <value>");
713 	    notify(side, "\"doctrine\" alone to see current settings");
714 	}
715 	return;
716     }
717     notify_doctrine(side, cmdargstr);
718 }
719 
720 void
do_down(Side * side)721 do_down(Side *side)
722 {
723     cmd_error(side, "not yet implemented");
724 }
725 
726 void
do_draw_willingness(Side * side)727 do_draw_willingness(Side *side)
728 {
729     DURING_GAME_ONLY(side);
730     if (side->prefixarg < 0)
731       side->prefixarg = 1;
732     net_set_willing_to_draw(side, (side->prefixarg ? 1 : 0));
733 }
734 
735 void
do_embark(Side * side)736 do_embark(Side *side)
737 {
738     DURING_GAME_ONLY(side);
739     apply_to_all_selected_units(side, do_one_embark);
740 }
741 
742 static int
do_one_embark(Side * side,Unit * unit)743 do_one_embark(Side *side, Unit *unit)
744 {
745     Unit *transport;
746 
747     transport = embarkation_unit(unit);
748     if (transport != NULL) {
749 	net_prep_enter_action(unit, unit, transport);
750 	return TRUE;
751     }
752     cmd_error(side, "Nothing for %s to enter!", unit_handle(side, unit));
753     return FALSE;
754 }
755 
756 /* Command to end the side's activity for the turn. */
757 
758 void
do_end_turn(Side * side)759 do_end_turn(Side *side)
760 {
761     DURING_GAME_ONLY(side);
762     net_finish_turn(side);
763 }
764 
765 void
do_force_global_replan(Side * side)766 do_force_global_replan(Side *side)
767 {
768     Unit *unit;
769 
770     DURING_GAME_ONLY(side);
771     for_all_side_units(side, unit) {
772 	if (in_play(unit) && unit->plan != NULL) {
773 	    net_force_replan(unit);
774 	}
775     }
776 }
777 
778 void
do_give(Side * side)779 do_give(Side *side)
780 {
781     DURING_GAME_ONLY(side);
782     if (nummtypes == 0) {
783 	cmd_error(side, "No materials in this game!");
784 	return;
785     }
786     apply_to_all_selected_units(side, do_one_give);
787 }
788 
789 static short *gt_amts;
790 static short *gt_rslts;
791 
792 static int
do_one_give(Side * side,Unit * unit)793 do_one_give(Side *side, Unit *unit)
794 {
795     short m;
796     Unit *unit2;
797 
798     if (gt_amts == NULL)
799       gt_amts = (short *) xmalloc(nummtypes * sizeof(short));
800     if (gt_rslts == NULL)
801       gt_rslts = (short *) xmalloc(nummtypes * sizeof(short));
802 
803     for_all_material_types(m)
804       gt_amts[m] = side->prefixarg;
805     unit2 = give_supplies(unit, gt_amts, gt_rslts);
806     report_give(side, unit, unit2, gt_rslts);
807     return TRUE;
808 }
809 
810 /* Tell the unit to sit around for a given number of turns. */
811 
812 void
do_idle(Side * side)813 do_idle(Side *side)
814 {
815     DURING_GAME_ONLY(side);
816     apply_to_all_selected_units(side, do_one_idle);
817 }
818 
819 static int
do_one_idle(Side * side,Unit * unit)820 do_one_idle(Side *side, Unit *unit)
821 {
822     if (side->prefixarg < 0)
823       side->prefixarg = 9999;
824     net_set_sentry_task(unit, side->prefixarg);
825     return TRUE;
826 }
827 
828 /* Release a controlled side to act independently. */
829 
830 void
do_release(Side * side)831 do_release(Side *side)
832 {
833     Side *side2;
834 
835     DURING_GAME_ONLY(side);
836     if (empty_string(cmdargstr)) {
837 	cmd_error(side, "No side given to release!");
838 	return;
839     }
840     side2 = parse_side_spec(cmdargstr);
841     if (side2 == NULL) {
842 	cmd_error(side, "Can't get a side from \"%s\"!", cmdargstr);
843 	return;
844     }
845     if (side2 == side) {
846 	cmd_error(side, "Can't release ourselves!?");
847 	return;
848     }
849     if (side2->controlled_by != side) {
850 	cmd_error(side, "You don't control %s!", short_side_title(side2));
851 	return;
852     }
853     /* Modify the controllee's bit, not the controller's. */
854     net_set_controlled_by(side2, side, FALSE);
855 }
856 
857 /* Set a side's current research topic. */
858 
859 void
do_research(Side * side)860 do_research(Side *side)
861 {
862     int a;
863 
864     DURING_GAME_ONLY(side);
865     if (numatypes == 0) {
866 	cmd_error(side, "No research in this game.");
867 	return;
868     }
869     if (!empty_string(cmdargstr)) {
870 	if (!g_side_can_research()) {
871 	    cmd_error(side, "Side cannot do research.");
872 	    return;
873 	}
874 	if (strcmp(cmdargstr, "nothing") == 0) {
875 	    notify(side, "Your wise men will rest now.");
876 	    net_set_side_research_topic(side, NONATYPE);
877 	    return;
878 	}
879 	a = parse_advance_spec(cmdargstr);
880 	if (a == -1) {
881 	    cmd_error(side, "Not a valid advance: \"%s\"", cmdargstr);
882 	    return;
883 	} else if (a == -2) {
884 	    cmd_error(side, "Ambiguous: \"%s\"", cmdargstr);
885 	    return;
886 	}
887 	notify(side, "Your wise men will search for the secret of %s.",
888 	       a_type_name(a));
889 	net_set_side_research_topic(side, a);
890 	return;
891     } else {
892 	int i = 0;
893         char abuf[BUFSIZE];
894 
895 	if (g_side_can_research()) {
896 	    a = side->research_topic;
897 	    if (a == NOADVANCE) {
898 		notify(side, "Your wise men are waiting for a research area.");
899 	    } else if (a == NONATYPE) {
900 		notify(side, "Your wise men are resting.");
901 	    } else {
902 		notify(side,
903 		       "Your wise men are %d/%d of the way to achieving %s.",
904 		       side->advance[a], a_rp(a), a_type_name(a));
905 	    }
906 	}
907 	/* List the available advances. */
908 	/* (should be a separate function perhaps) */
909 	strcpy(abuf, "Next advances: ");
910 	for_all_advance_types(a) {
911 	    if (side_can_research(side, a)) {
912 		if (i > 0)
913 		  strcat(abuf, ", ");
914 		strcat(abuf, a_type_name(a));
915 		++i;
916 		if (i == 4) {
917 		    notify(side, "%s", abuf);
918 		    abuf[0] = '\0';
919 		    i = 0;
920 		}
921 	    }
922 	}
923 	if (i > 0)
924 	  notify(side, "%s", abuf);
925     }
926 }
927 
928 /* Given a string, find an advance whose name at least partially matches. */
929 
930 int
parse_advance_spec(char * str)931 parse_advance_spec(char *str)
932 {
933     int a, rslt = -1;
934 
935     for_all_advance_types(a) {
936 	if (strstr(a_type_name(a), str)) {
937 	    if (rslt >= 0) {
938 		return -2;
939 	    }
940 	    rslt = a;
941 	}
942     }
943     return rslt;
944 }
945 
946 /* Make the selected units sleep just for the remainder of the current
947    turn. */
948 
949 void
do_reserve(Side * side)950 do_reserve(Side *side)
951 {
952     DURING_GAME_ONLY(side);
953     apply_to_all_selected_units(side, do_one_reserve);
954 }
955 
956 static int
do_one_reserve(Side * side,Unit * unit)957 do_one_reserve(Side *side, Unit *unit)
958 {
959     net_set_unit_reserve(side, unit, TRUE, FALSE);
960     return TRUE;
961 }
962 
963 /* Set up tasks to resupply the selected units. */
964 
965 /* (should warn if task is likely to fail) */
966 
967 void
do_return(Side * side)968 do_return(Side *side)
969 {
970     DURING_GAME_ONLY(side);
971     apply_to_all_selected_units(side, do_one_return);
972 }
973 
974 static int
do_one_return(Side * side,Unit * unit)975 do_one_return(Side *side, Unit *unit)
976 {
977     /* (should doublecheck range and error out if no chance) */
978     net_set_resupply_task(unit, NONMTYPE);
979     return TRUE;
980 }
981 
982 /* Auto-finish turns, for the given number of turns. */
983 
984 void
do_run(Side * side)985 do_run(Side *side)
986 {
987     int turns;
988 
989     DURING_GAME_ONLY(side);
990     if (!empty_string(cmdargstr))
991       turns = atoi(cmdargstr);
992     else
993       turns = side->prefixarg;
994     if (turns < 0)
995       turns = 0;
996     notify(side, "Running free for %d turn%s.",
997 	   turns, (turns == 1 ? "" : "s"));
998     if (turns > 0) {
999 	net_set_autofinish(side, TRUE);
1000 	autofinish_start = g_turn();
1001 	autofinish_count = turns;
1002     }
1003 }
1004 
1005 /* Set the rate at which AIs move their units. */
1006 
1007 void
do_set_rate(Side * side)1008 do_set_rate(Side *side)
1009 {
1010     int slow, fast;
1011     char *reststr;
1012 
1013     DURING_GAME_ONLY(side);
1014     if (!empty_string(cmdargstr)) {
1015 	slow = strtol(cmdargstr, &reststr, 10);
1016 	fast = strtol(reststr, &reststr, 10);
1017 	set_play_rate(slow, fast);
1018     }
1019 }
1020 
1021 /* Put a unit to sleep, which means it is inactive each turn until it
1022    is woken up, either by some alarm going off (such as low supply) or
1023    by explicit player action. */
1024 
1025 void
do_sleep(Side * side)1026 do_sleep(Side *side)
1027 {
1028     DURING_GAME_ONLY(side);
1029     apply_to_all_selected_units(side, do_one_sleep);
1030 }
1031 
1032 static int
do_one_sleep(Side * side,Unit * unit)1033 do_one_sleep(Side *side, Unit *unit)
1034 {
1035     net_set_unit_asleep(side, unit, TRUE, FALSE);
1036     return TRUE;
1037 }
1038 
1039 /* Submit to (become controlled by) the specified side. */
1040 
1041 void
do_submit(Side * side)1042 do_submit(Side *side)
1043 {
1044     Side *side2;
1045 
1046     DURING_GAME_ONLY(side);
1047     if (side->controlled_by != NULL) {
1048 	cmd_error(side, "You are already controlled by %s!",
1049 		  short_side_title(side->controlled_by));
1050     }
1051     if (empty_string(cmdargstr)) {
1052 	cmd_error(side, "No side given to submit to!");
1053 	return;
1054     }
1055     side2 = parse_side_spec(cmdargstr);
1056     if (side2 == NULL) {
1057 	cmd_error(side, "Can't get a side from \"%s\"!", cmdargstr);
1058 	return;
1059     }
1060     if (side2 == side) {
1061 	cmd_error(side, "Can't submit to ourselves!?");
1062 	return;
1063     }
1064     net_set_controlled_by(side, side2, TRUE);
1065     /* (should draw emblem inverted underneath controller's emblem) */
1066 }
1067 
1068 /* Take supplies from transport. */
1069 
1070 void
do_take(Side * side)1071 do_take(Side *side)
1072 {
1073     DURING_GAME_ONLY(side);
1074     if (nummtypes == 0) {
1075 	cmd_error(side, "No materials in this game!");
1076 	return;
1077     }
1078     apply_to_all_selected_units(side, do_one_take);
1079 }
1080 
1081 static int
do_one_take(Side * side,Unit * unit)1082 do_one_take(Side *side, Unit *unit)
1083 {
1084     short m, needed;
1085 
1086     if (gt_amts == NULL)
1087       gt_amts = (short *) xmalloc(nummtypes * sizeof(short));
1088     if (gt_rslts == NULL)
1089       gt_rslts = (short *) xmalloc(nummtypes * sizeof(short));
1090 
1091     for_all_material_types(m)
1092       gt_amts[m] = side->prefixarg;
1093     needed = take_supplies(unit, gt_amts, gt_rslts);
1094     report_take(side, unit, needed, gt_rslts);
1095     return TRUE;
1096 }
1097 
1098 void
do_trust(Side * side)1099 do_trust(Side *side)
1100 {
1101     Side *side2;
1102 
1103     DURING_GAME_ONLY(side);
1104     if (empty_string(cmdargstr)) {
1105 	notify_relationships(side);
1106 	return;
1107     }
1108     side2 = parse_side_spec(cmdargstr);
1109     if (side2 == NULL) {
1110 	cmd_error(side, "No side matching \"%s\"", cmdargstr);
1111 	return;
1112     }
1113     if (side2 == side) {
1114 	cmd_error(side, "We're not confused about trusting ourselves!");
1115 	return;
1116     }
1117     net_set_trust(side, side2, 1);
1118 }
1119 
1120 void
do_wake(Side * side)1121 do_wake(Side *side)
1122 {
1123     DURING_GAME_ONLY(side);
1124     apply_to_all_selected_units(side, do_one_wake);
1125 }
1126 
1127 static int
do_one_wake(Side * side,Unit * unit)1128 do_one_wake(Side *side, Unit *unit)
1129 {
1130     net_wake_unit(side, unit, FALSE);
1131     /* If an argument was given, apply to all "top-level" units within
1132        the radius specified by the argument. */
1133     if (side->prefixarg >= 0)
1134       net_wake_area(side, unit->x, unit->y, side->prefixarg, FALSE);
1135     return 1;
1136 }
1137 
1138 void
do_wake_all(Side * side)1139 do_wake_all(Side *side)
1140 {
1141     DURING_GAME_ONLY(side);
1142     apply_to_all_selected_units(side, do_one_wake_all);
1143 }
1144 
1145 static int
do_one_wake_all(Side * side,Unit * unit)1146 do_one_wake_all(Side *side, Unit *unit)
1147 {
1148     net_wake_unit(side, unit, TRUE);
1149     /* If an argument was given, apply to all units and occs within
1150        the radius specified by the argument. */
1151     if (side->prefixarg >= 0)
1152       net_wake_area(side, unit->x, unit->y, side->prefixarg, TRUE);
1153     return 1;
1154 }
1155 
1156 void
do_warning_log(Side * side)1157 do_warning_log(Side *side)
1158 {
1159     warnings_logged = !warnings_logged;
1160     if (warnings_logged)
1161       notify_all("Warnings now being logged in \"Xconq.Warnings\".\n");
1162     else
1163       notify_all("Warnings will not be logged.\n");
1164 }
1165 
1166 #ifdef DESIGNERS
1167 
1168 void
do_gdl(Side * side)1169 do_gdl(Side *side)
1170 {
1171     /* (should check designer status?) */
1172     if (!empty_string(cmdargstr))
1173       interp_form(NULL, read_form_from_string(cmdargstr, NULL, NULL, NULL));
1174     /* (should broadcast to all?) */
1175 }
1176 
1177 #endif /* DESIGNERS */
1178 
1179 #ifdef DEBUGGING
1180 
1181 /* Debugging-related commands. */
1182 
1183 /* General debugging toggles. */
1184 
1185 void
do_debug(Side * side)1186 do_debug(Side *side)
1187 {
1188 #ifndef Debug
1189     toggle_debugging(&Debug);
1190 #endif
1191     notify_all("Debugging: %d", Debug);
1192     update_everything();
1193 }
1194 
1195 void
do_debugg(Side * side)1196 do_debugg(Side *side)
1197 {
1198 #ifndef DebugG
1199     toggle_debugging(&DebugG);
1200 #endif
1201     notify_all("Graphics debugging: %d", DebugG);
1202     update_everything();
1203 }
1204 
1205 void
do_debugm(Side * side)1206 do_debugm(Side *side)
1207 {
1208 #ifndef DebugM
1209     toggle_debugging(&DebugM);
1210 #endif
1211     notify_all("Machine play/AI debugging: %d", DebugM);
1212     update_everything();
1213 }
1214 
1215 #endif /* DEBUGGING */
1216