]> icculus.org git repositories - btb/d2x.git/blob - main/cmd.c
implement wait command
[btb/d2x.git] / main / cmd.c
1 #ifdef HAVE_CONFIG_H
2 #include <conf.h>
3 #endif
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9
10 #include "pstypes.h"
11 #include "cmd.h"
12 #include "console.h"
13 #include "error.h"
14 #include "u_mem.h"
15 #include "strutil.h"
16 #include "weapon.h"
17 #include "key.h"
18
19
20 typedef struct cmd_s
21 {
22         char          *name;
23         cmd_handler_t function;
24         struct cmd_s  *next;
25 } cmd_t;
26
27 /* The list of cmds */
28 static cmd_t *cmd_list = NULL;
29
30
31 #define ALIAS_NAME_MAX 32
32 typedef struct cmd_alias_s
33 {
34         char           name[ALIAS_NAME_MAX];
35         char           *value;
36         struct cmd_alias_s *next;
37 } cmd_alias_t;
38
39 /* The list of aliases */
40 static cmd_alias_t *cmd_alias_list = NULL;
41
42
43 /* The list of keybindings */
44 static char *cmd_keybinding_list[256];
45
46
47 /* add a new console command */
48 void cmd_addcommand(char *cmd_name, cmd_handler_t cmd_func)
49 {
50         cmd_t *cmd;
51
52         Assert(cmd_name != NULL);
53
54         for (cmd = cmd_list; cmd; cmd = cmd->next) {
55                 if (!stricmp(cmd_name, cmd->name))
56                 {
57                         Int3();
58                         con_printf(CON_NORMAL, "command %s already exists, not adding\n", cmd_name);
59                         return;
60                 }
61         }
62
63         /* create command, insert at front of list */
64         MALLOC(cmd, cmd_t, 1);
65         cmd->name = cmd_name;
66         cmd->function = cmd_func;
67         cmd->next = cmd_list;
68         con_printf(CON_DEBUG, "cmd_addcommand: added %s\n", cmd->name);
69         cmd_list = cmd;
70 }
71
72
73 typedef struct cmd_queue_s
74 {
75         char *command_line;
76         struct cmd_queue_s *next;
77 } cmd_queue_t;
78
79 /* The list of commands to be executed */
80 static cmd_queue_t *cmd_queue_head = NULL;
81 static cmd_queue_t *cmd_queue_tail = NULL;
82
83
84 /* execute a parsed command */
85 void cmd_execute(int argc, char **argv)
86 {
87         cmd_t *cmd;
88         cmd_alias_t *alias;
89
90         for (cmd = cmd_list; cmd; cmd = cmd->next) {
91                 if (!stricmp(argv[0], cmd->name)) {
92                         con_printf(CON_DEBUG, "cmd_execute: executing %s\n", argv[0]);
93                         return cmd->function(argc, argv);
94                 }
95         }
96
97         for (alias = cmd_alias_list; alias; alias = alias->next) {
98                 if (!stricmp(argv[0], alias->name)) {
99                         con_printf(CON_DEBUG, "cmd_execute: pushing alias \"%s\": %s\n", alias->name, alias->value);
100                         cmd_insert(alias->value);
101                         return;
102                 }
103         }
104
105         /* Otherwise */
106         if (argc > 1)  // set value of cvar
107                 cvar_set(argv[0], argv[1]);
108         con_printf(CON_VERBOSE, "%s: %f\n", argv[0], cvar(argv[0]));
109 }
110
111
112 /* Parse an input string */
113 void cmd_parse(char *input)
114 {
115         char buffer[CMD_MAX_LENGTH];
116         char *tokens[CMD_MAX_TOKENS];
117         int num_tokens;
118         int i, l;
119         int quoted = 0;
120
121         Assert(input != NULL);
122         
123         /* Strip leading spaces */
124         for (i=0; isspace(input[i]); i++) ;
125         strncpy( buffer, &input[i], CMD_MAX_LENGTH );
126
127         //printf("lead strip \"%s\"\n",buffer);
128         l = strlen(buffer);
129         /* If command is empty, give up */
130         if (l==0) return;
131
132         /* Strip trailing spaces */
133         for (i=l-1; i>0 && isspace(buffer[i]); i--) ;
134         buffer[i+1] = 0;
135         //printf("trail strip \"%s\"\n",buffer);
136
137         /* Split into tokens */
138         l = strlen(buffer);
139         num_tokens = 1;
140
141         tokens[0] = buffer;
142         for (i=1; i<l; i++) {
143                 if (buffer[i] == '"') {
144                         quoted = 1 - quoted;
145                         continue;
146                 }
147                 if ((isspace(buffer[i]) || buffer[i] == '=') && !quoted) {
148                         buffer[i] = 0;
149                         while (isspace(buffer[i+1]) && (i+1 < l)) i++;
150                         tokens[num_tokens++] = &buffer[i+1];
151                 }
152         }
153
154         /* Check for matching commands */
155         cmd_execute(num_tokens, tokens);
156 }
157
158
159 int cmd_queue_wait = 0;
160
161 void cmd_queue_process()
162 {
163         cmd_queue_t *cmd;
164
165         while (!cmd_queue_wait && cmd_queue_head) {
166                 cmd = cmd_queue_head;
167                 cmd_queue_head = cmd_queue_head->next;
168                 if (!cmd_queue_head)
169                         cmd_queue_tail = NULL;
170
171                 con_printf(CON_DEBUG, "cmd_queue_process: processing %s\n", cmd->command_line);
172                 cmd_parse(cmd->command_line);  // Note, this may change the queue
173
174                 d_free(cmd->command_line);
175                 d_free(cmd);
176         }
177
178         if (cmd_queue_wait) {
179                 con_printf(CON_DEBUG, "cmd_queue_process: waiting\n");
180                 cmd_queue_wait--;
181                 return;
182         }
183 }
184
185
186 /* Add some commands to the queue to be executed */
187 void cmd_enqueue(int insert, char *input)
188 {
189         cmd_queue_t *new, *head, *tail;
190         char *line;
191
192         Assert(input != NULL);
193         head = tail = NULL;
194
195         while (*input) {
196                 int quoted = 0;
197
198                 /* Strip leading spaces */
199                 while(isspace(*input) || *input == ';')
200                         input++;
201
202                 /* If command is empty, give up */
203                 if (! *input)
204                         continue;
205
206                 /* Now at start of a command line */
207                 line = input;
208
209                 /* Find the end of this line (\n, ;, or nul) */
210                 do {
211                         if (!*input)
212                                 break;
213                         if (*input == '"') {
214                                 quoted = 1 - quoted;
215                                 continue;
216                         } else if ( *input == '\n' || (!quoted && *input == ';') ) {
217                                 *input = 0;
218                         }
219                 } while (*input++);
220
221                 /* make a new queue item, add it to list */
222                 MALLOC(new, cmd_queue_t, 1);
223                 new->command_line = d_strdup(line);
224                 new->next = NULL;
225
226                 if (!head)
227                         head = new;
228                 if (tail)
229                         tail->next = new;
230                 tail = new;
231
232                 con_printf(CON_DEBUG, "cmd_enqueue: adding %s\n", line);
233         }
234
235         if (insert) {
236                  /* add our list to the head of the main list */
237                 if (cmd_queue_head)
238                         tail->next = cmd_queue_head;
239                 if (!cmd_queue_tail)
240                         cmd_queue_tail = tail;
241                 
242                 cmd_queue_head = head;
243                 con_printf(CON_DEBUG, "cmd_enqueue: added to front of list\n");
244         } else {
245                 /* add our list to the tail of the main list */
246                 if (!cmd_queue_head)
247                         cmd_queue_head = head;
248                 if (cmd_queue_tail)
249                         cmd_queue_tail->next = head;
250                 
251                 cmd_queue_tail = tail;
252                 con_printf(CON_DEBUG, "cmd_enqueue: added to back of list\n");
253         }
254 }
255
256 void cmd_enqueuef(int insert, char *fmt, ...)
257 {
258         va_list arglist;
259         char buf[CMD_MAX_LENGTH];
260         
261         va_start (arglist, fmt);
262         vsnprintf (buf, CMD_MAX_LENGTH, fmt, arglist);
263         va_end (arglist);
264         
265         cmd_enqueue(insert, buf);
266 }
267
268
269 /* Attempt to autocomplete an input string */
270 char *cmd_complete(char *input)
271 {
272         cmd_t *ptr;
273         cmd_alias_t *aptr;
274
275         int len = strlen(input);
276
277         if (!len)
278                 return NULL;
279
280         for (ptr = cmd_list; ptr != NULL; ptr = ptr->next)
281                 if (!strnicmp(input, ptr->name, len))
282                         return ptr->name;
283
284         for (aptr = cmd_alias_list; aptr != NULL; aptr = aptr->next)
285                 if (!strnicmp(input, aptr->name, len))
286                         return aptr->name;
287
288         return cvar_complete(input);
289 }
290
291
292 int cmd_handle_keybinding(unsigned char key)
293 {
294         if (cmd_keybinding_list[key]) {
295                 cmd_insert(cmd_keybinding_list[key]);
296                 return 1;
297         }
298         return 0;
299 }
300
301
302
303 /* alias */
304 void cmd_alias(int argc, char **argv)
305 {
306         cmd_alias_t *alias;
307         char buf[CMD_MAX_LENGTH] = "";
308         int i;
309
310         if (argc < 2)
311         {
312                 con_printf(CON_NORMAL, "aliases:\n");
313                 for (alias = cmd_alias_list; alias; alias = alias->next)
314                         con_printf(CON_NORMAL, "%s: %s\n", alias->name, alias->value);
315                 return;
316         }
317
318         for (i = 2; i < argc; i++) {
319                 if (i > 2)
320                         strncat(buf, " ", CMD_MAX_LENGTH);
321                 strncat(buf, argv[i], CMD_MAX_LENGTH);
322         }
323
324         for (alias = cmd_alias_list; alias; alias = alias->next) {
325                 if (!stricmp(argv[1], alias->name))
326                 {
327                         d_free(alias->value);
328                         alias->value = d_strdup(buf);
329                         return;
330                 }
331         }
332
333         MALLOC(alias, cmd_alias_t, 1);
334         strncpy(alias->name, argv[1], ALIAS_NAME_MAX);
335         alias->value = d_strdup(buf);
336         alias->next = cmd_alias_list;
337         cmd_alias_list = alias;
338 }
339
340 /* bind */
341 /* FIXME: key_text is not really adequate for this */
342 void cmd_bind(int argc, char **argv)
343 {
344         char buf[CMD_MAX_LENGTH] = "";
345         unsigned char key = 0;
346         int i;
347
348         if (argc < 2)
349         {
350                 con_printf(CON_NORMAL, "key bindings:\n");
351                 for (i = 0; i < 256; i++) {
352                         if (!cmd_keybinding_list[i])
353                                 continue;
354                         con_printf(CON_NORMAL, "%s: %s\n", key_text[i], cmd_keybinding_list[i]);
355                 }
356                 return;
357         }
358
359         for (i = 2; i < argc; i++) {
360                 if (i > 2)
361                         strncat(buf, " ", CMD_MAX_LENGTH);
362                 strncat(buf, argv[i], CMD_MAX_LENGTH);
363         }
364
365         for (i = 0; i < 256; i++) {
366                 if (!stricmp(argv[1], key_text[i])) {
367                         key = i;
368                         break;
369                 }
370         }
371
372         if (!key) {
373                 con_printf(CON_CRITICAL, "bind: key %s not found\n", argv[1]);
374                 return;
375         }
376
377         if (cmd_keybinding_list[key])
378                 d_free(cmd_keybinding_list[key]);
379         cmd_keybinding_list[key] = d_strdup(buf);
380 }
381
382 /* +/- actions */
383 int Console_button_states[CMD_NUM_BUTTONS];
384 void cmd_attack_on(int argc, char **argv) { Console_button_states[CMD_ATTACK] = 1; }
385 void cmd_attack_off(int argc, char **argv) { Console_button_states[CMD_ATTACK] = 0; }
386 void cmd_attack2_on(int argc, char **argv) { Console_button_states[CMD_ATTACK2] = 1; }
387 void cmd_attack2_off(int argc, char **argv) { Console_button_states[CMD_ATTACK2] = 0; }
388
389 /* weapon select */
390 void cmd_impulse(int argc, char**argv)
391 {
392         if (argc < 2)
393                 return;
394         int n = atoi(argv[1]);
395         if (n >= 1 && n <= 20) {
396                 select_weapon((n-1) % 10, (n-1) / 10, 0, 1);
397         }
398 }
399
400 /* echo to console */
401 void cmd_echo(int argc, char **argv)
402 {
403         char buf[CMD_MAX_LENGTH] = "";
404         int i;
405         for (i = 1; i < argc; i++) {
406                 if (i > 1)
407                         strncat(buf, " ", CMD_MAX_LENGTH);
408                 strncat(buf, argv[i], CMD_MAX_LENGTH);
409         }
410         con_printf(CON_NORMAL, "%s\n", buf);
411 }
412
413 /* execute script */
414 void cmd_exec(int argc, char **argv) {
415         cmd_queue_t *new, *head, *tail;
416         PHYSFS_File *f;
417         char line[CMD_MAX_LENGTH] = "";
418
419         if (argc < 2)
420                 return;
421
422         head = tail = NULL;
423
424         f = PHYSFSX_openReadBuffered(argv[1]);
425         if (!f) {
426                 con_printf(CON_CRITICAL, "exec: %s not found\n", argv[1]);
427                 return;
428         }
429         while (PHYSFSX_gets(f, line)) {
430                 /* make a new queue item, add it to list */
431                 MALLOC(new, cmd_queue_t, 1);
432                 new->command_line = d_strdup(line);
433                 new->next = NULL;
434
435                 if (!head)
436                         head = new;
437                 if (tail)
438                         tail->next = new;
439                 tail = new;
440
441                 con_printf(CON_DEBUG, "cmd_exec: adding %s\n", line);
442         }
443         PHYSFS_close(f);
444
445         /* add our list to the head of the main list */
446         if (cmd_queue_head)
447                 tail->next = cmd_queue_head;
448         if (!cmd_queue_tail)
449                 cmd_queue_tail = tail;
450
451         cmd_queue_head = head;
452         con_printf(CON_DEBUG, "cmd_exec: added to front of list\n");
453 }
454
455
456 /* execute script */
457 void cmd_wait(int argc, char **argv)
458 {
459         if (argc < 2)
460                 cmd_queue_wait = 1;
461         else
462                 cmd_queue_wait = atoi(argv[1]);
463 }
464
465
466 void cmd_free(void)
467 {
468         int i;
469         void *p, *temp;
470
471         p = cmd_list;
472         while (p) {
473                 temp = p;
474                 p = ((cmd_t *)p)->next;
475                 d_free(temp);
476         }
477
478         p = cmd_alias_list;
479         while (p) {
480                 d_free(((cmd_alias_t *)p)->value);
481                 temp = p;
482                 p = ((cmd_alias_t *)p)->next;
483                 d_free(temp);
484         }
485
486         for (i = 0; i < 256; i++)
487                 if (cmd_keybinding_list[i])
488                         d_free(cmd_keybinding_list[i]);
489 }
490
491
492 void cmd_init(void)
493 {
494         memset(Console_button_states, 0, sizeof(int) * CMD_NUM_BUTTONS);
495
496         cmd_addcommand("alias", cmd_alias);
497         cmd_addcommand("bind", cmd_bind);
498
499         cmd_addcommand("+attack", cmd_attack_on);
500         cmd_addcommand("-attack", cmd_attack_off);
501         cmd_addcommand("+attack2", cmd_attack2_on);
502         cmd_addcommand("-attack2", cmd_attack2_off);
503
504         cmd_addcommand("impulse", cmd_impulse);
505
506         cmd_addcommand("echo", cmd_echo);
507
508         cmd_addcommand("exec", cmd_exec);
509
510         cmd_addcommand("wait", cmd_wait);
511
512         atexit(cmd_free);
513 }