1
/*------------------------------------------------------------------
2
Copyright 1989 Kevin P. Smith
5
Permission to use, copy, modify, and distribute this
6
software and its documentation for any purpose and without
7
fee is hereby granted, provided that the above copyright
8
notice appear in all copies.
12
Permission to use, copy, modify, and distribute this software and
13
its documentation, or any derivative works thereof, for any
14
NON-COMMERCIAL purpose and without fee is hereby granted, provided
15
that this copyright notice appear in all copies. No
16
representations are made about the suitability of this software for
17
any purpose. This software is provided "as is" without express or
20
Xtrek Copyright 1986 Chris Guthrie
21
Netrek (Xtrek II) Copyright 1989 Kevin P. Smith
23
Paradise II (Netrek II) Copyright 1993 Larry Denys
26
Copyright 2000 Bob Glamm
28
--------------------------------------------------------------------*/
30
/* was control_mess.c, but changed to message.c for a smaller filename (BG) */
34
Ugh, this code is in the middle of a rewrite.
36
It used to use a tokenizer with a global dictionary to split the
37
input into words. The tokenizer accepted abbreviations as long as
38
these were unique. However, adding a new word to the dictionary
39
would often cause old abbreviations to be invalidated.
41
I wrote a new parser that was called as each token needed to be
42
extracted. This used a dictionary that was local to each submenu.
43
This localizes changes to the menu structure so that effects of
44
adding new commands are minimized.
46
Some of the file is converted to use this, but not all. Eventually
47
the entire module will use the context-sensitive tokenizer.
113
INDTOK, /* these need to be adjacent and in order */
114
FEDTOK, /* these need to be adjacent and in order */
115
ROMTOK, /* these need to be adjacent and in order */
116
KLITOK, /* these need to be adjacent and in order */
117
ORITOK, /* these need to be adjacent and in order */
121
SCOUTTOK, /* these need to be adjacent and in order */
122
DESTROYERTOK, /* these need to be adjacent and in order */
123
CRUISERTOK, /* these need to be adjacent and in order */
124
BATTLESHIPTOK, /* these need to be adjacent and in order */
125
ASSAULTTOK, /* these need to be adjacent and in order */
126
STARBASETOK, /* these need to be adjacent and in order */
127
ATTTOK, /* these need to be adjacent and in order */
128
JUMPSHIPTOK, /* these need to be adjacent and in order */
129
FRIGATETOK, /* these need to be adjacent and in order */
130
WARBASETOK, /* these need to be adjacent and in order */
143
ALLOWTOK, /* control allow [teams] */
148
static int god_silent = 0;
150
/**********************************************************************/
152
/* New parsing method. */
155
char *literal; /* the command they should type */
156
enum token_names_e tok; /* what the parser should return */
157
char *doc; /* documentation to print for a help command */
160
/* Scans the string cmd for the first whitespace delimited string.
161
Tries to match this string in a case-insensitive manner against the
162
list in legals. Returns the appropriate token. Alters the char*
163
pointed to by after to be the beginning of the next
164
whitespace-delimited string.
172
static enum token_names_e
173
next_token(char *cmd, struct control_cmd *legals, char **after)
175
char buf[80]; /* space for the token */
179
enum token_names_e potentialtok = ERRORTOK;
181
while (*cmd && isspace(*cmd))
187
for (s = buf; *cmd && !isspace(*cmd); s++, cmd++)
191
while (*cmd && isspace(*cmd))
195
*after = cmd; /* so they can find the next token */
197
for (i = 0; legals[i].literal; i++) {
198
int wordlen = strlen(buf);
199
if (0 == strncasecmp(buf, legals[i].literal, wordlen)) {
200
if (strlen(legals[i].literal) == wordlen) {
202
potentialtok = legals[i].tok;
203
break; /* exact match */
205
if (potentialtok != ERRORTOK) {
206
ambiguous = 1; /* this isn't the only match */
209
potentialtok = legals[i].tok;
217
match_token(char *cmd, char *token, char **after)
219
struct control_cmd legals[2];
220
legals[0].literal = token;
221
legals[0].tok = HELPTOK; /* pick any token but ERRORTOK */
222
legals[1].literal = 0;
223
return HELPTOK==next_token(cmd, legals, after);
226
/* Get a player slot number.
227
Returns -1 on failure, slot number on success.
228
Slot number is guaranteed to be <MAXPLAYER. */
231
get_slotnum(char *cmd, char **after)
234
while (*cmd && isspace(*cmd))
238
return -1; /* no token */
240
if (cmd[1] && !isspace(cmd[1]))
241
return -1; /* token too long */
243
if (*cmd>='0' && *cmd<='9')
245
else if (*cmd>='a' && *cmd<='z')
246
rval = *cmd-'a' + 10;
247
else if (*cmd>='A' && *cmd<='Z')
248
rval = *cmd-'A' + 10;
253
return -1; /* there aren't that many players */
256
/* scan to next token */
258
while (*cmd && isspace(*cmd))
266
/**********************************************************************/
269
respond(char *msg, int type)
274
pmessage2(msg, me->p_no, MINDIV, MCONTROL, 255);
278
bad_slotnum(char *msg)
281
sprintf(buf, "`%s' requires player slot number", msg);
286
/* Get a single token.
287
Returns 0 on failure, 1 on success.
288
Token is returned in dst. */
291
get_one_token(char *cmd, char *dst, int dstsize, char **after)
293
while (*cmd && isspace(*cmd))
297
return 0; /* no token */
299
while (dstsize>1 && *cmd && !isspace(*cmd)) {
306
/* scan to next token */
307
while (*cmd && isspace(*cmd))
316
Integer is returned in dst.
317
Returns 0 on failure without modifying dst.
321
get_int(char *cmd, int *dst, char **after)
325
if (1!=sscanf(cmd, " %i%n", &rval, &offset))
329
if (*cmd && !isspace(*cmd))
330
return 0; /* token wasn't all digits */
335
/* scan to next token */
336
while (*cmd && isspace(*cmd))
345
Double is returned in dst.
346
Returns 0 on failure without modifying dst.
350
get_double(char *cmd, double *dst, char **after)
355
if (1!=sscanf(cmd, " %lg%n", &rval, &offset))
359
if (*cmd && !isspace(*cmd))
360
return 0; /* token wasn't all digits */
365
/* scan to next token */
366
while (*cmd && isspace(*cmd))
375
get_teamid(char *cmd, int *team, char **after)
379
while (*cmd && isspace(*cmd)) {
382
if (cmd[3] && !isspace(cmd[3]))
383
return 0; /* too long */
387
for (i = -1; i < NUMTEAM; i++) {
389
if (0==strncasecmp(cmd, teams[j].shortname, 3)) {
397
/* scan to next token */
398
while (*cmd && isspace(*cmd))
407
get_shipid(char *cmd, int *shipn, char **after)
411
while (*cmd && isspace(*cmd)) {
416
for (i = 0; i < NUM_TYPES; i++) {
417
struct ship *ship = &shipvals[i];
419
len = strlen(ship->s_name);
420
if (0==strncasecmp(cmd, ship->s_name, len) &&
421
(cmd[len]==0 || isspace(cmd[len]))) {
425
} else if (tolower(cmd[0])==tolower(ship->s_desig1) &&
426
tolower(cmd[1])==tolower(ship->s_desig2) &&
427
(cmd[2]==0 || isspace(cmd[2]))) {
435
/* scan to next token */
436
while (*cmd && isspace(*cmd))
444
/* writes a comma-separated list of help strings into the message window */
447
respond_with_help_string(struct control_cmd *legals)
450
char buf[65]; /* leave space for the message prefix */
452
strcpy(buf, "Available commands: ");
453
for (i = 0; legals[i].literal; i++) {
454
if (!(legals[i].doc && legals[i].doc[0]))
456
if (strlen(buf) + 3 + strlen(legals[i].doc) > sizeof(buf)) {
459
if (!buf[0]) { /* one of the help strings was just too long */
460
respond("ACK! programmer error: help string too long", 0);
466
strcat(buf, legals[i].doc);
467
if (legals[i+1].literal)
475
* Here we handle the controls on players.
476
* If you add something, make sure you place it in the help.
477
* Thanks, have a nice day.
481
parse_control_player(char *cmd)
485
struct player *victim;
486
int godliness = me->p_stats.st_royal - GODLIKE + 1;
488
static struct control_cmd available_cmds[] = {
489
{"help", HELPTOK, 0},
490
{"die", DIETOK, "die"},
491
{"eject", EJECTTOK, "eject"},
492
{"armies", ARMIESTOK, "armies [%d=5]"},
493
{"plasma", PLASMATOK, "plasma [%d]"},
494
{"missiles", MISSILETOK, "missiles [%d=max]"},
495
{"team", TEAMTOK, "team <teamstr>"},
496
{"ship", SHIPTOK, "ship <shiptype>"},
497
{"rank", RANKTOK, "rank (+|-|%d)"},
498
{"royal", ROYALTOK, "royal (+|-|%d)"},
499
{"kills", KILLSTOK, "kills (+|-|%d)"},
500
{"hose", HOSETOK, "hose"},
501
{"move", MOVETOK, "move %d %d"},
505
pnum = get_slotnum(cmd, &cmd);
507
bad_slotnum("control player");
510
victim = &players[pnum];
512
if (victim->p_status == PFREE) {
513
respond("Slot is not alive.", 1);
518
* These would probably work better as pointers to functions instead of
519
* a giant switch, but what the hell, I'm lazy.
520
* Maybe I'll change it later.
523
switch (next_token(cmd, available_cmds, &arg)) {
525
victim->p_ship.s_type = STARBASE;
526
victim->p_whydead = KPROVIDENCE;
527
victim->p_explode = 10;
528
victim->p_status = PEXPLODE;
529
victim->p_whodead = 0;
531
sprintf(buf, "%s (%2s) was utterly obliterated by %s (%2s).",
532
victim->p_name, twoletters(victim),
533
me->p_name, twoletters(me));
534
pmessage(buf, 0, MALL, MCONTROL);
539
victim->p_ship.s_type = STARBASE;
540
victim->p_whydead = KQUIT;
541
victim->p_explode = 10;
542
victim->p_status = PEXPLODE;
543
victim->p_whodead = 0;
546
"%s (%2s) has been ejected from the game by %s (%2s).",
547
victim->p_name, twoletters(victim),
548
me->p_name, twoletters(me));
549
pmessage(buf, 0, MALL, MCONTROL);
556
if (*arg && !get_int(arg, &armies, (char**)0)) {
557
respond("optional arg to `control player <slotnum> armies` must be integer", 0);
560
victim->p_armies += armies;
562
sprintf(buf, "%s (%2s) has been given %d armies by %s (%2s).",
563
victim->p_name, twoletters(victim), armies,
564
me->p_name, twoletters(me));
565
pmessage(buf, 0, MALL, MCONTROL);
573
if (*arg && !get_int(arg, &yes, (char**)0)) {
574
respond("optional arg to `control player <slotnum> plasma` must be integer", 0);
579
victim->p_ship.s_nflags |= SFNPLASMAARMED;
581
victim->p_ship.s_nflags &= ~SFNPLASMAARMED;
584
sprintf(buf, "%s (%2s) has been %s plasma torps by %s (%2s).",
585
victim->p_name, twoletters(victim),
586
yes ? "given" : "denied",
587
me->p_name, twoletters(me));
588
pmessage(buf, 0, MALL, MCONTROL);
595
int yes = shipvals[victim->p_ship.s_type].s_missilestored;
597
if (*arg && !get_int(arg, &yes, (char**)0)) {
598
respond("optional arg to `control player <slotnum> missile` must be integer", 0);
603
victim->p_ship.s_nflags |= SFNHASMISSILE;
604
victim->p_ship.s_missilestored = yes;
607
victim->p_ship.s_nflags &= ~SFNHASMISSILE;
611
sprintf(buf, "%s (%2s) has been %s %d missiles by %s (%2s).",
612
victim->p_name, twoletters(victim),
613
yes ? "given" : "denied",
614
yes, me->p_name, twoletters(me));
615
pmessage(buf, 0, MALL, MCONTROL);
624
if (!get_teamid(arg, &team, (char**)0)) {
625
respond("available teams: FED ORI ROM KLI IND", 0);
628
team = idx_to_mask(team);
630
victim->p_hostile |= victim->p_team;
631
victim->p_team = team;
632
victim->p_hostile &= ~team;
633
victim->p_swar &= ~team;
634
sprintf(buf, "%s (%2s) has been changed to a %s by %s (%2s).",
635
victim->p_name, twoletters(victim), teams[team].nickname,
636
me->p_name, twoletters(me));
638
pmessage(buf, 0, MALL, MCONTROL);
645
if (!get_shipid(arg, &ship, (char**)0)) {
646
respond("available ships: SC DD CA AS BB SB AT JS FR WB CL CV SUPER", 0);
649
/* If others are docked, then kick them off */
650
if (allows_docking(victim->p_ship)) {
652
for (i = 0; i < victim->p_ship.s_numports; i++) {
653
base_undock(victim, i);
656
get_ship_for_player(victim, ship);
657
switch_special_weapon();
658
victim->p_flags &= ~PFENG;
659
sprintf(buf, "%s (%2s) has been changed to a %c%c by %s (%2s).",
660
victim->p_name, twoletters(victim),
661
victim->p_ship.s_desig1, victim->p_ship.s_desig2,
662
me->p_name, twoletters(me));
664
pmessage(buf, 0, MALL, MCONTROL);
670
int rank = victim->p_stats.st_rank;
672
if (match_token(arg, "+", (char**)0))
674
else if (match_token(arg, "-", (char**)0))
676
else if (!get_int(arg, &rank, (char**)0)) {
677
respond("Try: control player %d rank [%d]+-", 0);
683
if (rank >= NUMRANKS)
686
victim->p_stats.st_rank = rank;
687
sprintf(buf, "%s (%2s) has been given a rank of %s by %s (%2s).",
688
victim->p_name, twoletters(victim),
689
ranks[victim->p_stats.st_rank].name,
690
me->p_name, twoletters(me));
692
pmessage(buf, 0, MALL, MCONTROL);
698
int rank = victim->p_stats.st_royal;
700
if (match_token(arg, "+", (char**)0))
702
else if (match_token(arg, "-", (char**)0))
704
else if (!get_int(arg, &rank, (char**)0)) {
705
respond("Try: control player %d royal [%d]+-", 0);
711
if (rank >= NUMROYALRANKS)
712
rank = NUMROYALRANKS - 1;
714
if (rank>=GODLIKE && godliness < 2) {
715
respond("You aren't powerful enough to grant godlike royalty.",
719
victim->p_stats.st_royal = rank;
720
sprintf(buf, "%s (%2s) has been given a rank of %s by %s (%2s).",
721
victim->p_name, twoletters(victim),
722
royal[victim->p_stats.st_royal].name,
723
me->p_name, twoletters(me));
725
pmessage(buf, 0, MALL, MCONTROL);
731
double kills = victim->p_kills;
733
if (match_token(arg, "+", (char**)0))
735
else if (match_token(arg, "-", (char**)0)) {
737
if (kills<0) kills=0;
738
} else if (!get_double(arg, &kills, (char**)0)) {
739
respond("Try: control player %d kills [%f]+-", 0);
743
victim->p_kills = kills;
744
sprintf(buf, "%s (%2s) has been given %f kills by %s (%2s).",
745
victim->p_name, twoletters(victim),
746
kills, me->p_name, twoletters(me));
748
pmessage(buf, 0, MALL, MCONTROL);
753
victim->p_shield = 0;
754
victim->p_damage = victim->p_ship.s_maxdamage / 2;
755
sprintf(buf, "%s (%2s) has been hosed by %s (%2s).",
756
victim->p_name, twoletters(victim),
757
me->p_name, twoletters(me));
759
pmessage(buf, 0, MALL, MCONTROL);
766
if (! (get_int(arg, &x, &s) && get_int(s, &y, (char**)0))) {
767
respond("Try: control player %d move %d %d", 0);
771
if (x <= 0 || y <= 0 || x >= 200000 || y >= 200000) {
772
respond("You want to move him where?", 0);
777
sprintf(buf, "%s (%2s) has been moved to %d %d by %s (%2s).",
778
victim->p_name, twoletters(victim),
780
me->p_name, twoletters(me));
782
pmessage(buf, 0, MALL, MCONTROL);
786
case HELPTOK: /* fall through */
788
respond_with_help_string(available_cmds);
794
parse_control(char *str)
797
struct player *victim;
799
int godliness = me->p_stats.st_royal - GODLIKE + 1;
801
static struct control_cmd available_cmds[] = {
802
{"help", HELPTOK, 0},
803
{"freeslot", FREESLOTTOK, "freeslot %p"},
804
{"player", PLAYERTOK, "player ..."},
805
{"robot", ROBOTTOK, "robot [args]"},
806
{"snake", SNAKETOK, "snake [args]"},
807
{"quiet", QUIETTOK, "quiet"},
808
{"nukegame", NUKEGAMETOK, "nukegame"},
809
{"restart", RESTARTTOK, "restart"},
810
{"galaxy", NEWGALAXY, "galaxy"},
811
{"shiptimer", SHIPTIMERTOK, "shiptimer (teamstr|shipstr)*"},
812
{"allow", ALLOWTOK, "allow [teams]"},
817
if (godliness <= 0) {
818
return 0; /* "fail" silently. Don't advertise divine
819
powers to peasants. */
823
switch (next_token(str, available_cmds, &nexttoken)) {
826
int slot = get_slotnum(nexttoken, (char**)0);
828
respond("\"control freeslot\" requires slot number.", 0);
831
victim = &players[slot];
832
if (victim->p_ntspid)
833
kill(victim->p_ntspid, SIGHUP);
835
victim->p_status = PFREE;
836
victim->p_ntspid = 0;
839
sprintf(buf, "Player slot %s has been freed by %s (%2s).",
840
nexttoken, me->p_name, twoletters(me));
841
pmessage(buf, 0, MALL, MCONTROL);
854
for (s = nexttoken; get_teamid(s, &team, &s); ) {
855
newlock |= idx_to_mask(team);
858
respond("Usage: control allow [fed] [rom] [kli] [ori]", 0);
863
status2->nontteamlock = newlock;
864
strcpy(buf, "Allowed teams now set to:");
865
if (status2->nontteamlock == ALLTEAM) {
866
strcat(buf, " <all teams>");
869
if (status2->nontteamlock & FED)
871
if (status2->nontteamlock & ROM)
873
if (status2->nontteamlock & KLI)
875
if (status2->nontteamlock & ORI)
882
return parse_control_player(nexttoken);
892
argv[0] = build_path(ROBOT);
897
argv[i] = malloc(size);
898
if (!get_one_token(s, argv[i], size, &s))
900
realloc(argv[i], strlen(argv[i])+1);
905
execvp(argv[0], argv);
906
fprintf(stderr, "Ack! Unable to exec %s\n", argv[0]);
910
respond("Unable to fork robot", 0);
913
sprintf(buf, "Robot forked (pid %d) with arguments %s",
928
argv[0] = build_path(SNAKE);
933
argv[i] = malloc(size);
934
if (!get_one_token(s, argv[i], size, &s))
936
realloc(argv[i], strlen(argv[i])+1);
941
execvp(argv[0], argv);
942
fprintf(stderr, "Ack! Unable to exec %s\n", argv[0]);
946
respond("Unable to fork snake", 0);
949
sprintf(buf, "Snake forked (pid %d) with arguments %s",
958
respond("No sneaking allowed", 0);
961
sprintf(buf, "Switching to %s mode.", god_silent ? "loud" : "quiet");
963
god_silent = !god_silent;
966
warning("Nuking game. Have a nice day.");
968
sprintf(buf, "The game has been nuked by %s (%2s).",
969
me->p_name, twoletters(me));
970
pmessage(buf, 0, MALL, MCONTROL);
972
kill(status->nukegame, 15);
975
warning("Attempting daemon restart.");
976
startdaemon(status2->league, 1);
979
explode_everyone(KPROVIDENCE, 0);
981
sprintf(buf, "The galaxy has been reset by %s (%2s).",
982
me->p_name, twoletters(me));
983
pmessage(buf, 0, MALL, MCONTROL);
985
status2->newgalaxy = 1;
986
warning("Creating new galaxy");
995
if (get_shipid(s, &j, &s))
997
else if (get_teamid(s, &j, &s))
998
teammask |= idx_to_mask(j);
1000
respond("Usage:", 0);
1001
respond("control shiptimers (fed|rom|kli|ori)* (sc|dd|ca|bb|as|sb|at|js|fr|wb)*", 0);
1002
respond(" resets the ship timers.", 0);
1007
for (i = 0; i < NUMTEAM; i++) {
1008
int teammask = idx_to_mask(i);
1009
if (teammask && !(teammask & (1 << i)))
1012
for (j = 0; j < NUM_TYPES; j++) {
1013
if (shipmask && !(shipmask & (1 << j)))
1016
if (teams[teammask].s_turns[j]) {
1017
sprintf(buf, "%s %s reset", teams[teammask].name, shipvals[j].s_name);
1019
teams[teammask].s_turns[j] = 0;
1025
case HELPTOK: /* fall through */
1027
respond_with_help_string(available_cmds);
1037
parse_info(char *cmd)
1042
static struct control_cmd available_cmds[] = {
1043
{"help", HELPTOK, 0},
1044
{"shiptimer", SHIPTIMERTOK, "shiptimer (teamstr|shipstr)*"},
1048
switch (next_token(cmd, available_cmds, &nexttoken)) {
1054
for (i = 0; i < NUMTEAM; i++) {
1055
int teammask = idx_to_mask(i);
1056
if (race && !(race & (1 << i)))
1058
for (j = 0; j < NUM_TYPES; j++) {
1059
if (teams[teammask].s_turns[j]) {
1060
sprintf(buf, "%s %s: %d minutes", teams[teammask].name,
1061
shipvals[j].s_name, teams[teammask].s_turns[j]);
1068
respond("All ships are available", 0);
1073
respond("Available subcommands: shiptimer", 0);
1079
parse_player(char *cmd)
1081
static int passver = 0;
1085
static struct control_cmd available_cmds[] = {
1086
{"help", HELPTOK, 0},
1087
{"password", PASSWDTOK, "password %s"},
1088
{"passwd", PASSWDTOK, 0},
1089
{"ratings", RATINGSTOK, "ratings"},
1090
{"rank", RANKTOK, "rank"},
1096
respond("Password change cancelled.", 0);
1102
switch (next_token(cmd, available_cmds, &nexttoken)) {
1105
static char newpass[16];
1107
if (me->p_pos < 0) { /* guest login */
1108
respond("You don't have a password!", 0);
1109
} else if (*nexttoken==0) {
1110
respond("\"player password\" requires new password as argument.", 0);
1111
respond(" example: \"player password lh4ern\"", 0);
1112
} else if (!passver) {
1113
memset(newpass, 0, 16);
1114
strncpy(newpass, crypt(nexttoken, me->p_name), 15);
1115
respond("Repeat \"player password\" command to verify new password.", 0);
1116
respond(" or send \"player\" (no arguments) to cancel.", 0);
1123
memset(tmpbuf, 0, 16);
1124
strncpy(tmpbuf, crypt(nexttoken, me->p_name), 15);
1125
if (!strcmp(newpass, tmpbuf)) {
1126
/* perhaps it'd be better to put this part in */
1127
/* a different place */
1128
paths = build_path(PLAYERFILE);
1129
fd = open(paths, O_WRONLY, 0644);
1131
lseek(fd, 16 + me->p_pos * sizeof(struct statentry), 0);
1132
write(fd, newpass, 16);
1134
respond("Password changed.", 0);
1137
respond("open() of playerfile failed, password not changed.", 0);
1141
respond("Passwords did not match, password unchanged.", 0);
1146
case RATINGSTOK: /* print your ratings */
1149
compute_ratings(me, &r);
1151
sprintf(buf, "Bomb:%5.2f Plnts:%5.2f Rsrcs:%5.2f Dshs:%5.2f Offns:%5.2f", r.bombrat, r.planetrat, r.resrat, r.dooshrat, r.offrat);
1154
sprintf(buf, " JS:%5.2f SB:%5.2f WB:%5.2f Ratio:%5.2f", r.jsrat, r.sbrat, r.wbrat, r.ratio);
1157
sprintf(buf, "Overall Ratings: Battle:%5.2f Strat:%5.2f Spec. Ship:%5.2f", r.battle, r.strategy, r.special);
1162
case RANKTOK: /* print the requirements for the next rank */
1165
rank = me->p_stats.st_rank;
1166
strcpy(buf, "Your current rank is ");
1167
strcat(buf, ranks[rank].name);
1169
if (rank == NUMRANKS - 1)
1172
sprintf(buf, "To make the next rank (%s) you need:",
1173
ranks[rank + 1].name);
1176
sprintf(buf, " Genocides: %d DI: %.2f Battle: %.2f",
1177
ranks[rank + 1].genocides, ranks[rank + 1].di,
1178
ranks[rank + 1].battle);
1181
sprintf(buf, " Strategy: %.2f Spec. Ships: %.2f",
1182
ranks[rank + 1].strategy, ranks[rank + 1].specship);
1189
respond_with_help_string(available_cmds);
1199
umpire_speak(char *msg)
1201
pmessage(msg, -1, MALL, UMPIRE);
1205
talk_about_team(struct league_team *team, char *type)
1208
struct player *captain;
1210
if (team->captain >= 0)
1211
captain = &players[team->captain];
1215
sprintf(buf, "The %s team is named `%s'.", type, team->name);
1219
sprintf(buf, " %s (%s) is their captain.", captain->p_name,
1220
twoletters(captain));
1222
strcpy(buf, " They have not chosen a captain yet.");
1225
if (team->index >= 0) {
1226
sprintf(buf, " They have chosen the %s",
1227
teams[idx_to_mask(team->index)].name);
1230
strcpy(buf, " They have not chosen an empire yet.");
1236
team_really_ready(struct league_team *team)
1238
if (team->index < 0) {
1239
respond("You haven't chosen an empire", 1);
1242
if (team->name[0] == 0) {
1243
respond("You haven't chosen a name", 1);
1250
trydefect(struct player *victim, enum HomeAway dest, char *destname,
1251
enum HomeAway from, char *fromname, struct player *actor)
1254
struct league_team *fromteam =
1255
(from == AWAY) ? &status2->away : &status2->home;
1257
if (victim->p_status==PFREE) {
1258
respond("Uh, he's not in the game.",1);
1262
if (victim->p_homeaway == dest) {
1263
sprintf(buf, "%s already belong to the %s team",
1264
actor == victim ? "You" : "They", destname);
1268
if (actor->p_homeaway != from) {
1269
sprintf(buf, "You don't belong to the %s team. You can't kick him off.",
1274
if (fromteam->captain == actor->p_no) {
1275
if (victim == actor) {
1276
if (status2->league > 1 || status2->home.ready || status2->away.ready) {
1277
respond("You can't defect in the middle of the game. You're the captain!", 1);
1280
sprintf(buf, "%s (%s), the captain of the %s team, has defected!",
1281
victim->p_name, twoletters(victim), fromname);
1285
sprintf(buf, "%s (%s) has kicked %s (%s) off his team.",
1286
actor->p_name, twoletters(actor),
1287
victim->p_name, twoletters(victim));
1292
if (victim == actor) {
1293
if (status2->league > 1 || status2->home.ready || status2->away.ready) {
1294
respond("Only the captain can kick you off now.", 1);
1297
sprintf(buf, "%s (%s) has defected to the %s team!",
1298
victim->p_name, twoletters(victim),
1303
respond("Only the captain can kick other people off the team.", 1);
1307
victim->p_homeaway = dest;
1308
victim->p_status = PEXPLODE;
1309
victim->p_whydead = KPROVIDENCE;
1310
victim->p_explode = 1;
1314
parse_league(char *subcommand)
1316
struct league_team *myteam, *otherteam;
1322
static char captain_only[] = "That command is reserved for team captains.";
1323
static struct control_cmd available_cmds[] = {
1324
{"help", HELPTOK, 0},
1325
{"captain", CAPTAINTOK, "captain [%d]"},
1326
{"time", TIMETOK, "time [%d %d]"},
1327
{"pass", PASSTOK, "pass"},
1328
{"start", STARTTOK, "start [%d]"},
1329
{"restart", RESTARTTOK, "restart [%d]"},
1331
{"timeout", TIMEOUTTOK, "timeout [%d]"},
1333
{"teamname", TEAMNAMETOK, "teamname %s"},
1334
{"information", INFOTOK, "information"},
1335
{"away", AWAYTOK, "away [%d]"},
1336
{"home", HOMETOK, "home [%d]"},
1337
{"newgalaxy", NEWGALAXY, "newgalaxy [%d]"},
1338
{"pause", PAUSETOK, "pause"},
1339
{"continue", CONTINUETOK, "continue"},
1340
{"maxplayer", MAXPLAYERTOK, "maxplayer [%d]"},
1341
{"freeslot", FREESLOTTOK, "freeslot %d"},
1345
switch (me->p_homeaway) {
1347
myteam = &status2->home;
1348
otherteam = &status2->away;
1352
myteam = &status2->away;
1353
otherteam = &status2->home;
1357
respond("WHOA! internal error. You aren't on a team!", 0);
1358
respond("I'm afraid I'm going to have to kill you", 0);
1359
me->p_status = PEXPLODE;
1364
iscaptain = (myteam->captain == me->p_no);
1366
/********************/
1368
if (get_teamid(subcommand, &i, (char**)0)) {
1370
respond(captain_only, 1);
1373
if (status2->league != 1) {
1374
respond("The game has started. You can't change your mind now.", 1);
1377
if ((myteam->ready || otherteam->ready) && myteam->index >= 0) {
1378
respond("One of the teams is ready. You can't change your mind now.", 1);
1381
if (otherteam->index >= 0) {
1382
if (i == otherteam->index) {
1383
respond("The other team has already chosen that empire", 1);
1388
if (me->p_homeaway == HOME) {
1389
if (!status2->awaypassed) {
1390
respond("Away team gets first choice of empire.", 1);
1394
else /* away */ if (myteam->index >= 0 && 0 == status2->awaypassed) {
1395
respond("Give the other team a chance to choose a side, will ya?", 1);
1398
else if (status2->awaypassed == 1) {
1399
respond("You passed the choice of empire. You have to wait for their choice.", 1);
1404
if (i == myteam->index) {
1405
respond("That already IS your empire.", 1);
1409
respond("You can't change your mind without a reset. Ask for one", 1);
1413
sprintf(buf, "The %s team has chosen the %s for their empire.",
1414
teamtype, teams[idx_to_mask(i)].name);
1420
} else switch (next_token(subcommand, available_cmds, &nexttoken)) {
1422
case HELPTOK: /********************/
1424
respond_with_help_string(available_cmds);
1427
respond("Available commands: captain [ %d ], time, information, maxplayer.", 0);
1431
case CAPTAINTOK: /********************/
1434
i = !get_int(nexttoken, &j, (char**)0) || j;
1437
if (myteam->captain < 0 ||
1438
players[myteam->captain].p_status != PALIVE ||
1439
players[myteam->captain].p_team != me->p_team) {
1440
if (myteam->captain >= 0) {
1442
safety valve in case the person is ghostbusted or on
1445
sprintf(buf, "%s has been overthrown as captain of the %s team",
1446
players[myteam->captain].p_name, teamtype);
1449
respond("OK. *POOF* you're a captain!", 1);
1450
sprintf(buf, "%s (%s) is now fearless leader of the %s team!",
1451
me->p_name, twoletters(me), teamtype);
1453
myteam->captain = me->p_no;
1455
else if (iscaptain) {
1456
respond("Listen, silly. You already are captain. No point in rubbing it in", 1);
1459
/* if myteam->captain were <0, we wouldn't get here */
1460
struct player *capn = &players[myteam->captain];
1461
sprintf(buf, "Sorry, %s (%s) is already captain of your team.",
1462
capn->p_name, twoletters(capn));
1468
respond("Wimp. We didn't want you for captain anyway.", 1);
1469
sprintf(buf, "%s (%s) has chickened out.",
1470
me->p_name, twoletters(me));
1472
sprintf(buf, "Who now will lead the %s team?", teamtype);
1474
myteam->captain = -1;
1477
respond("You can't quit being a captain. You weren't one in the first place.", 1);
1482
case TIMETOK: /********************/
1483
if (0==*nexttoken) {
1484
switch (status2->league) {
1486
sprintf(buf, "%d seconds left in pre-tourney warm-up.",
1487
status2->leagueticksleft / SECONDS(1));
1490
sprintf(buf, "%d minutes left in regulation play.",
1491
status2->leagueticksleft / MINUTES(1));
1494
sprintf(buf, "%d minutes left in overtime.",
1495
status2->leagueticksleft / MINUTES(1));
1498
sprintf(buf, "game is configured for %d minutes (%d overtime).",
1499
configvals->regulation_minutes, configvals->overtime_minutes);
1505
else if (!iscaptain) {
1506
respond(captain_only, 1);
1509
else if (status2->league != 1) {
1510
respond("You can only adjust the time parameters during the configuration phase", 1);
1513
else if (otherteam->ready) {
1514
respond("The other team is ready to start. You can't change the game params NOW.", 1);
1517
else if (!get_int(nexttoken,&myteam->desired.regulation, &nexttoken)
1518
|| !get_int(nexttoken, &myteam->desired.overtime, (char**)0)){
1519
respond("Usage: time [ %d %d ]", 1);
1523
if (status2->home.desired.regulation == status2->away.desired.regulation
1524
&& status2->home.desired.overtime == status2->away.desired.overtime) {
1525
configvals->regulation_minutes = status2->home.desired.regulation;
1526
configvals->overtime_minutes = status2->home.desired.overtime;
1527
sprintf(buf, "The captains have agreed to a %d minute game (%d overtime).", configvals->regulation_minutes, configvals->overtime_minutes);
1531
sprintf(buf, "The %s team wishes a game of %d minutes (%d overtime)",
1532
teamtype, myteam->desired.regulation, myteam->desired.overtime);
1538
case PASSTOK: /********************/
1539
if (status2->league != 1) {
1540
respond("The time for that is long past.", 1);
1544
respond(captain_only, 1);
1547
if (me->p_homeaway == AWAY && status2->awaypassed) {
1548
respond("You already passed the choice of empire.", 1);
1551
else if (me->p_homeaway == HOME && status2->awaypassed == 0) {
1552
respond("You can't possibly pass the choice of empire. You don't HAVE it!", 1);
1555
else if (status2->awaypassed > 1) {
1556
respond("You both passed already, so get on with it. (indecisive wishy-washy cretins)", 1);
1559
status2->awaypassed++;
1561
sprintf(buf, "The %s team has passed the choice of empire", teamtype);
1564
if (status2->awaypassed > 1) {
1565
umpire_speak("Computer choosing randomly for both teams");
1566
if (status2->home.index < 0) {
1567
status2->home.index = lrand48() % ((status2->away.index < 0) ? 4 : 3);
1568
if (status2->away.index >= 0 &&
1569
status2->home.index >= status2->away.index)
1570
status2->home.index++;
1572
if (status2->away.index < 0) {
1573
status2->away.index = lrand48() % 3;
1574
if (status2->away.index >= status2->home.index)
1575
status2->away.index++;
1580
case STARTTOK: /********************/
1581
if (status2->league != 1) {
1582
respond("The game has already started.", 1);
1586
respond(captain_only, 1);
1589
if (get_int(nexttoken, &myteam->ready, (char**)0) && !myteam->ready) {
1590
sprintf(buf, "The %s team is not ready.", teamtype);
1595
if (!team_really_ready(myteam)) {
1596
respond("Your team is not really ready. You need a name and an empire.", 0);
1601
if (otherteam->ready && !team_really_ready(otherteam)) {
1602
otherteam->ready = 0;
1603
sprintf(buf, "The %s team was ready but the other wasn't.", teamtype);
1606
sprintf(buf, "The %s team is ready to start now.", teamtype);
1611
if (otherteam->ready) {
1612
/* shit! we're good to go! */
1614
umpire_speak("Both sides are ready. Let the carnage begin!");
1615
umpire_speak("Everybody dies. T-mode starts in 1 minute.");
1616
status2->league = 2;
1617
status2->leagueticksleft = 60 * TICKSPERSEC;
1621
explode_everyone(KTOURNSTART, 0);
1625
case RESTARTTOK: /********************/
1626
if (status2->league != 1) {
1627
respond("The game has started. You can't change your mind now.", 1);
1632
respond(captain_only, 1);
1636
myteam->desired.restart = !get_int(nexttoken, &i, (char**)0) || i;
1638
sprintf(buf, myteam->desired.restart ?
1639
"%s (%s) would like to restart team selection." :
1640
"%s (%s) is satisfied with the teams.",
1641
me->p_name, twoletters(me));
1644
if (status2->home.desired.restart && status2->away.desired.restart) {
1645
umpire_speak("Both captains have agreed to restart team selection.");
1647
status2->awaypassed = 0;
1648
status2->home.index = status2->away.index = -1;
1650
status2->home.ready = status2->away.ready = 0;
1652
status2->home.desired.restart = status2->away.desired.restart = 0;
1661
case TEAMNAMETOK: /********************/
1662
if (status2->league != 1) {
1663
respond("The game has started. You can't change your mind now.", 1);
1667
respond(captain_only, 1);
1670
if (0==*nexttoken) {
1671
respond("What do you want to call your team?\n", 1);
1674
strncpy(myteam->name, nexttoken, sizeof(myteam->name));
1675
myteam->name[sizeof(myteam->name) - 1] = 0;
1677
sprintf(buf, "Henceforth let the %s team be known as `%s'!",
1678
teamtype, myteam->name);
1683
case INFOTOK: /********************/
1684
sprintf(buf, "The game will last for %d minutes (%d overtime)",
1685
configvals->regulation_minutes,
1686
configvals->overtime_minutes);
1688
sprintf(buf, "Teams are limited to %d players on the field at once",
1689
configvals->playersperteam);
1691
sprintf(buf, "You are on the %s team.", teamtype);
1693
talk_about_team(&status2->home, "home");
1694
talk_about_team(&status2->away, "away");
1696
if (status2->awaypassed > 1)
1697
umpire_speak("Both teams passed empire choice. Computer assigned.");
1698
else if (status2->awaypassed)
1699
umpire_speak("Away has passed empire choice to the Home team");
1701
umpire_speak("Away chooses empire first");
1706
case AWAYTOK: /********************/
1708
struct player *victim;
1712
int idx = get_slotnum(nexttoken, (char**)0);
1714
respond("`league away' requires a valid slot number", 0);
1717
victim = &players[idx];
1720
trydefect(victim, AWAY, "away", HOME, "home", me);
1724
case HOMETOK: /********************/
1726
struct player *victim;
1730
int idx = get_slotnum(nexttoken, (char**)0);
1732
respond("`league away' requires a valid slot number", 0);
1735
victim = &players[idx];
1738
trydefect(victim, HOME, "home", AWAY, "away", me);
1742
case NEWGALAXY: /********************/
1744
if (status2->league != 1) {
1745
respond("The game has started. You can't change your mind now.", 1);
1749
if (myteam->ready || otherteam->ready) {
1750
respond("You can't reset the galaxy now. We're almost ready!", 1);
1754
respond(captain_only, 1);
1760
myteam->desired.galaxyreset =
1761
!get_int(nexttoken, &j, (char**)0) || j;
1764
if (myteam->desired.galaxyreset) {
1765
sprintf(buf, "%s (%s) is dissatisfied with the galaxy",
1766
me->p_name, twoletters(me));
1769
sprintf(buf, "%s (%s) thinks the galaxy is just fine, thank you.",
1770
me->p_name, twoletters(me));
1774
if (status2->home.desired.galaxyreset &&
1775
status2->away.desired.galaxyreset) {
1776
umpire_speak("Both captains have agreed that the galaxy sucks.");
1777
status2->newgalaxy = 1;
1778
warning("Creating new galaxy");
1780
status2->home.desired.galaxyreset = status2->away.desired.galaxyreset = 0;
1782
status2->awaypassed = 0;
1783
status2->home.index = status2->away.index = -1;
1784
status2->home.ready = status2->away.ready = 0;
1785
status2->home.desired.restart = status2->away.desired.restart = 0;
1790
case PAUSETOK: /********************/
1792
respond(captain_only, 1);
1796
myteam->desirepause = 1;
1798
if (status2->home.desirepause && status2->away.desirepause) {
1799
/* well, it's unanimous! */
1800
status2->paused = SECONDS(10);
1801
umpire_speak("The game has been paused");
1804
sprintf(buf, "The %s team wishes to PAUSE the game.", teamtype);
1808
status2->pausemsgfuse = 0;
1812
case CONTINUETOK: /********************/
1814
respond(captain_only, 1);
1818
myteam->desirepause = 0;
1820
sprintf(buf, "The %s team wishes to CONTINUE.", teamtype);
1823
status2->pausemsgfuse = 0;
1830
if (!get_int(nexttoken, &mp, (char**)0)) {
1831
sprintf(buf, "The game is currently configured for a maximum of %d players on each", configvals->playersperteam);
1833
respond("team.", 0);
1834
if (configvals->playersperteam != myteam->desired.maxplayers) {
1835
sprintf(buf, "You, however, want it to be %d.",
1836
myteam->desired.maxplayers);
1842
respond(captain_only, 1);
1847
respond("That's a stupid value for players-per-team.", 1);
1851
myteam->desired.maxplayers = mp;
1852
if (status2->away.desired.maxplayers
1853
== status2->home.desired.maxplayers) {
1854
configvals->playersperteam = mp;
1855
sprintf(buf, "Captains have agreed to limit players per team to %d", mp);
1859
sprintf(buf, "The %s team would like to limit the number of ",
1862
sprintf(buf, "players per team to %d", mp);
1871
struct player *victim;
1872
slotnum = get_slotnum(nexttoken, (char**)0);
1874
respond("freeslot requires slot number", 1);
1878
victim = &players[slotnum];
1879
if (victim->p_ntspid)
1880
kill(victim->p_ntspid, SIGHUP);
1882
victim->p_status = PFREE;
1883
victim->p_ntspid = 0;
1885
sprintf(buf, "Player slot %s has been freed by %s (%2s).",
1886
nexttoken, me->p_name, twoletters(me));
1899
* Parse command messages.
1903
parse_command_mess(char *str, unsigned char who)
1907
int godlike = me->p_stats.st_royal - GODLIKE + 1;
1909
static struct control_cmd available_cmds[] = {
1910
{"help", HELPTOK, 0},
1911
{"control", CONTROLTOK, "control ..."},
1912
{"league", LEAGUETOK, "league ..."},
1913
{"version", VERSIONTOK, "version"},
1914
{"player", PLAYERTOK, "player ..."},
1915
{"queue", QUEUETOK, "queue"}, {"tq", QUEUETOK, 0},
1916
{"information", INFOTOK, "information ..."},
1917
{"observe", OBSERVETOK, "observe"},
1918
{"parameters", PARAMTOK, "parameters"}, {"params", PARAMTOK, 0},
1919
{"cluecheck", CLUECHECKTOK, "cluecheck [%s]"},
1926
switch (next_token(str, available_cmds, &nexttoken)) {
1928
sprintf(buf, "Available commands: %s%s%s",
1929
godlike ? "control ..., " : "",
1930
status2->league ? "league ..., " :
1932
"version, player ..., queue,");
1934
respond(" information ..., observe [%d], parameters, cluecheck [%s], help", 0);
1937
if (status2->league) {
1938
respond("God controls are disabled during league play.", 1);
1940
return parse_control(nexttoken);
1944
sprintf(buf, "NetrekII (Paradise), %s", PARAVERS);
1949
return parse_player(nexttoken);
1952
if (me->p_status == PTQUEUE) {
1955
else if (status->tourn)
1956
respond("Dude, what are you waiting for!? It's T-mode. GO FIGHT!", 1);
1957
else if (!(me->p_flags & PFGREEN))
1958
respond("Can not enter the tourney queue while at alert", 1);
1959
else if (me->p_damage != 0 || me->p_shield < me->p_ship.s_maxshield)
1960
respond("Can not enter the tourney queue while damaged", 1);
1961
else if (me->p_armies > 0)
1962
respond("Can not take armies into the tourney queue", 1);
1965
well, stick them on the queue. They will be awoken when T
1971
/* need code to blab about this */
1972
me->p_status = PTQUEUE;
1973
sprintf(buf, "%s has entered the tournament queue to wait for T-mode", me->p_name);
1974
pmessage(buf, -1, MALL, "GOD->ALL");
1979
if (status2->league) {
1980
return parse_league(nexttoken);
1983
respond("League commands are disabled during non-league play.", 1);
1988
return parse_info(nexttoken);
1993
i = !get_int(nexttoken, &i, (char**)0) || i;
1996
if (me->p_observer && me->p_status == POBSERVE) {
1997
respond("You are already an observer.", 1);
2000
if (!(me->p_flags & PFGREEN))
2001
respond("Can not become an observer while at alert", 1);
2002
else if (me->p_damage != 0 || me->p_shield < me->p_ship.s_maxshield)
2003
respond("Can not become an observer while damaged", 1);
2004
else if (me->p_armies > 0)
2005
respond("Can not become an observer while carrying armies", 1);
2008
me->p_status = POBSERVE;
2014
if (me->p_observer && me->p_status == POBSERVE) {
2016
if (!(status2->league && status->tourn))
2024
warning("Transmitting new game parameters");
2028
#if defined(CLUECHECK1) || defined(CLUECHECK2)
2029
return accept_cluecheck(nexttoken);