NXEngine v1.0.0.6
[NXEngine.git] / console.cpp
blobaa7e85d30fe0f14582851bea9bdeb14bdeb573ad
2 #include "nx.h"
3 #include <stdarg.h>
4 #include "console.fdh"
6 static CommandEntry commands[] =
8 "god", __god, 0, 1,
9 "script", __script, 1, 1,
10 "warp", __warp, 1, 999,
11 "sound", __sound, 1, 1,
12 "music", __music, 1, 1,
13 "giveweapon", __giveweapon, 1, 1,
14 "dropweapon", __dropweapon, 0, 1,
15 "level", __level, 1, 1,
16 "ammo", __ammo, 1, 1,
17 "maxammo", __maxammo, 1, 1,
18 "hp", __hp, 1, 1,
19 "maxhp", __maxhp, 1, 1,
20 "xp", __xp, 1, 1,
21 "spawn", __spawn, 1, 999,
22 "animate", __animate, 1, 2,
23 "infinitedamage", __infinitedamage, 0, 1,
24 "killall", __killall, 0, 0,
25 "movemode", __movemode, 1, 1,
26 "flag", __flag, 1, 1,
27 "clearflags", __clearflags, 0, 0,
28 "equip", __equip, 1, 2,
29 "giveitem", __giveitem, 1, 1,
30 "takeitem", __takeitem, 1, 1,
31 "qua", __qua, 0, 1,
32 "boa", __boa, 1, 1,
33 "cre", __cre, 0, 0,
34 "reset", __reset, 0, 0,
35 "fps", __fps, 0, 1,
37 "instant-quit", __set_iquit, 1, 1,
38 "no-quake-in-hell", __set_noquake, 1, 1,
39 "inhibit-fullscreen", __inhibit_fullscreen, 1, 1,
40 "emulate-bugs", __emulate_bugs, 1, 1,
41 "displayformat", __displayformat, 1, 1,
42 "skip-intro", __skip_intro, 1, 1,
44 "player->hide", __player_hide, 1, 1,
45 "player->inputs_locked", __player_inputs_locked, 1, 1,
46 "game.frozen", __game_frozen, 1, 1,
47 "textbox.SetVisible", __textbox_setvisible, 1, 1,
49 "hello", __hello, 0, 0,
50 "hi", __hello, 0, 0,
52 NULL, NULL
56 DebugConsole::DebugConsole()
58 fVisible = false;
59 fLineLen = 0;
60 fCursorTimer = 0;
61 fResponseTimer = 0;
63 fLine[0] = 0;
64 fResponse[0] = 0;
66 fKeyDown = 0;
67 fRepeatTimer = 0;
71 void c------------------------------() {}
74 void DebugConsole::SetVisible(bool newstate)
76 //stat("DebugConsole::SetVisible(%s)", newstate?"true":"false");
78 if (fVisible != newstate)
80 fVisible = newstate;
81 fKeyDown = 0;
82 fRepeatTimer = 0;
84 if (newstate)
86 fLine[0] = 0;
87 fLineLen = 0;
88 fBrowsingExpansion = false;
89 fBackIndex = fBackBuffer.CountItems();
91 fResponse[0] = 0;
92 fCursorTimer = 0;
97 bool DebugConsole::IsVisible()
99 return fVisible;
102 bool DebugConsole::HandleKey(int key)
104 if (!fVisible) return 0;
105 if (key != 9) fBrowsingExpansion = false;
107 if (key != fKeyDown)
109 fKeyDown = key;
110 fRepeatTimer = 25;
112 else
114 fRepeatTimer = 1;
117 fCursorTimer = 0;
119 switch(key)
121 case 27:
122 case '`':
124 SetVisible(false);
126 break;
128 case 13:
129 case 271: // numeric enter
131 SetVisible(false);
133 fLine[fLineLen] = 0;
134 Execute(fLine);
135 fLineLen = 0;
137 break;
139 case 10: break;
141 case 8:
143 if (fLineLen > 0)
144 fLineLen--;
146 break;
148 case 9: // command completion
150 fLine[fLineLen] = 0;
151 ExpandCommand();
153 fBrowsingExpansion = true;
154 fExpandIndex++;
156 break;
158 // command backbuffer
159 case SDLK_UP:
160 case SDLK_DOWN:
162 if (fBackBuffer.CountItems() > 0)
164 fBackIndex += (key == SDLK_UP) ? -1 : 1;
165 if (fBackIndex < 0) fBackIndex = (fBackBuffer.CountItems() - 1);
166 else fBackIndex %= fBackBuffer.CountItems();
168 const char *str = fBackBuffer.StringAt(fBackIndex);
170 maxcpy(fLine, str, sizeof(fLine) - 1);
171 fLineLen = strlen(str);
174 break;
176 default:
178 if (fLineLen < (sizeof(fLine) - 1))
179 fLine[fLineLen++] = key;
181 break;
184 return 1;
187 void DebugConsole::HandleKeyRelease(int key)
189 if (key == fKeyDown)
191 fKeyDown = 0;
192 fRepeatTimer = 0;
197 void c------------------------------() {}
200 void DebugConsole::Draw()
202 if (fResponse[0])
204 this->DrawText(fResponse);
206 if (--fResponseTimer <= 0)
207 fResponse[0] = 0;
209 else if (fVisible)
211 // key-repeat
212 if (fKeyDown)
214 if (--fRepeatTimer < 0)
215 HandleKey(fKeyDown);
218 char buffer[CONSOLE_MAXCMDLEN + 10];
219 fLine[fLineLen] = 0;
221 sprintf(buffer, "-> %s%c",
222 fLine, (fCursorTimer < 20) ? '_' : ' ');
224 this->DrawText(buffer);
226 if (++fCursorTimer > 30)
227 fCursorTimer = 0;
231 void DebugConsole::DrawText(const char *text)
233 font_draw_shaded(4, (SCREEN_HEIGHT - 16), text);
237 void c------------------------------() {}
240 void DebugConsole::Print(const char *fmt, ...)
242 va_list ar;
244 va_start(ar, fmt);
245 vsnprintf(fResponse, sizeof(fResponse), fmt, ar);
246 va_end(ar);
248 stat("%s", fResponse);
249 fResponseTimer = 60;
253 void c------------------------------() {}
256 bool DebugConsole::Execute(const char *line)
258 stat("DebugConsole::Execute('%s')", line);
260 // record command in backbuffer
261 if (fBackBuffer.CountItems() >= CONSOLE_MAX_BACK)
262 fBackBuffer.RemoveString(0);
263 fBackBuffer.AddString(line);
265 // split command into arguments
266 StringList args;
267 char *cmd = SplitCommand(line, &args);
269 if (cmd)
271 BList matches;
272 MatchCommand(cmd, &matches);
273 free(cmd);
275 if (matches.CountItems() == 1)
277 CommandEntry *command = (CommandEntry *)matches.ItemAt(0);
279 if (args.CountItems() < command->minArgs || \
280 args.CountItems() > command->maxArgs)
282 if (command->minArgs == command->maxArgs)
284 Print("'%s' requires %d argument%s", \
285 command->name, command->minArgs, \
286 (command->minArgs == 1) ? "":"s");
288 else if (args.CountItems() < command->minArgs)
290 Print("'%s' requires at least %d argument%s",
291 command->name, command->minArgs,
292 (command->minArgs == 1) ? "":"s");
294 else
296 Print("'%s' requires no more than %d arguments",
297 command->name, command->maxArgs);
300 else
302 void (*handler)(StringList *, int) = command->handler;
303 int num = (args.CountItems() > 0) ? atoi(args.StringAt(0)) : 0;
305 (*handler)(&args, num);
306 return 1;
309 else if (matches.CountItems() == 0)
311 Print("I don't understand");
313 else
315 Print("Ambiguous command");
319 return 0;
322 void DebugConsole::MatchCommand(const char *cmd, BList *matches)
324 for(int i=0; commands[i].name; i++)
326 if (strcasebegin(commands[i].name, cmd))
327 matches->AddItem(&commands[i]);
331 // split an input line into command and arguments
332 // returns the command portion of the line. you must free this buffer.
333 char *DebugConsole::SplitCommand(const char *line_in, StringList *args)
335 while(*line_in == ' ' || *line_in == '\t') line_in++;
336 char *line = strdup(line_in);
338 char *cmd = strtok(line, " \t");
339 if (cmd && cmd[0])
341 while(const char *arg = strtok(NULL, " \t"))
343 args->AddString(arg);
346 return line;
349 free(line);
350 return NULL;
353 // tab-expand the current command
354 void DebugConsole::ExpandCommand()
356 StringList args;
357 BList matches;
358 char *cmd;
360 fLine[fLineLen] = 0;
362 if (!fBrowsingExpansion)
364 maxcpy(fLineToExpand, fLine, sizeof(fLineToExpand));
365 fExpandIndex = 0;
368 cmd = SplitCommand(fLineToExpand, &args);
369 if (cmd)
371 MatchCommand(cmd, &matches);
372 free(cmd);
374 if (matches.CountItems() > 0)
376 if (fExpandIndex >= matches.CountItems())
377 fExpandIndex = 0;
379 CommandEntry *command = (CommandEntry *)matches.ItemAt(fExpandIndex);
380 DString newCommand(command->name);
382 for(int i=0;;i++)
384 const char *arg = args.StringAt(i);
385 if (!arg) break;
387 newCommand.AppendChar(' ');
388 newCommand.AppendString(arg);
391 if (args.CountItems() < command->minArgs)
392 newCommand.AppendChar(' ');
394 maxcpy(fLine, newCommand.String(), sizeof(fLine));
395 fLineLen = strlen(fLine);
399 if (matches.CountItems() != 1)
400 sound(SND_TINK);
404 void c------------------------------() {}
407 #define Respond console.Print
410 static void __god(StringList *args, int num)
412 bool enable;
414 if (args->CountItems() == 0)
415 enable = true;
416 else
417 enable = num;
419 game.debug.god = enable;
422 static void __script(StringList *args, int num)
424 // release any focus a current script may have on us
425 if (player->movementmode == MOVEMODE_NORMAL)
426 map_focus(NULL);
428 if (StartScript(num))
430 Respond("Script %04d started.", num);
432 else
434 Respond("No such script %04d", num);
438 static void __warp(StringList *args, int num)
440 if (num == 0)
442 DString stagename;
443 for(int i=0;i<args->CountItems();i++)
445 if (i != 0) stagename.AppendChar(' ');
446 stagename.AppendString(args->StringAt(i));
449 stat("Looking for '%s'", stagename.String());
450 for(num=0;;num++)
452 if (num >= num_stages)
454 if (!strcasecmp(stagename.String(), "village"))
456 num = 11;
458 else
460 Respond("Could determine stage number from your description.");
461 return;
464 break;
467 if (strcasebegin(stages[num].stagename, stagename.String()))
468 break;
472 game.switchstage.mapno = num;
473 game.switchstage.playerx = 16;
474 game.switchstage.playery = 16;
477 static void __sound(StringList *args, int num)
479 sound(num);
480 console.SetVisible(true); // keep console up
483 static void __music(StringList *args, int num)
485 extern const char *org_names[];
486 bool ok = true;
487 int i;
489 const char *name = args->StringAt(0);
490 if (num == 0 && strcmp(name, "0") != 0)
492 for(i=1;;i++)
494 if (!org_names[i]) break;
496 if (strcasebegin(org_names[i], name))
498 num = i;
499 break;
503 if (num == 0)
505 Respond("Don't know that song.");
506 return;
510 if (num < 0) ok = false;
511 else
513 for(i=1;i<=num;i++)
515 if (!org_names[i])
517 ok = false;
518 break;
523 if (!ok)
525 Respond("track out of range");
526 music(0);
528 else
530 music(0);
531 music(num);
532 if (org_names[num])
533 Respond("%s started", org_names[num]);
537 static void __giveweapon(StringList *args, int num)
539 if (num >= 0 && num < WPN_COUNT)
541 player->weapons[num].hasWeapon = 1;
542 player->weapons[num].maxammo = 0; // gives it unlimited ammo
543 player->weapons[num].ammo = 0;
544 player->curWeapon = num;
548 static void __dropweapon(StringList *args, int num)
550 if (args->CountItems() == 0)
551 num = player->curWeapon;
553 player->weapons[num].hasWeapon = 0;
554 player->weapons[num].maxammo = 0;
555 player->weapons[num].ammo = 0;
557 if (num == player->curWeapon)
558 stat_NextWeapon();
561 // set weapon level
562 static void __level(StringList *args, int num)
564 num--;
565 if (num < 0) num = 0;
566 if (num > 2) num = 2;
568 if (player->weapons[player->curWeapon].xp < 5)
569 player->weapons[player->curWeapon].xp = 5;
571 for(int timeout=0;timeout<500;timeout++)
573 if (player->weapons[player->curWeapon].level == num)
575 return;
577 else if (player->weapons[player->curWeapon].level < num)
579 AddXP(1);
581 else
583 SubXP(1);
587 Respond("Timeout");
591 static void __ammo(StringList *args, int num)
593 player->weapons[player->curWeapon].ammo = num;
594 if (player->weapons[player->curWeapon].ammo > player->weapons[player->curWeapon].maxammo)
595 player->weapons[player->curWeapon].maxammo = player->weapons[player->curWeapon].ammo;
598 static void __maxammo(StringList *args, int num)
600 player->weapons[player->curWeapon].maxammo = num;
601 if (player->weapons[player->curWeapon].ammo > player->weapons[player->curWeapon].maxammo)
602 player->weapons[player->curWeapon].ammo = player->weapons[player->curWeapon].maxammo;
605 static void __hp(StringList *args, int num)
607 player->hp = num;
608 if (player->hp > player->maxHealth)
609 player->maxHealth = player->hp;
612 static void __maxhp(StringList *args, int num)
614 player->maxHealth = num;
615 if (player->hp > player->maxHealth)
617 player->hp = player->maxHealth;
618 //PHealthBar.displayed_value = player->hp;
622 static void __xp(StringList *args, int num)
624 player->weapons[player->curWeapon].xp = num;
627 static void __spawn(StringList *args, int num)
629 int i = 0;
631 // if first argument is a number interpret it as a count of
632 // objects to spawn.
633 int count;
634 if (isdigit(args->StringAt(0)[0]))
636 count = num;
637 i++;
639 else
641 count = 1;
644 // reconstitute the arguments into the name of the object
645 // to be spawned.
646 DString objName;
647 int starti = i;
648 for(;;i++)
650 if (!args->StringAt(i)) break;
652 if (i > starti) objName.AppendChar(' ');
653 objName.AppendString(args->StringAt(i));
656 // try and get object type from the provided name
657 int type = ObjectNameToType(objName.String());
658 if (type == -1)
660 Respond("Unknown object. See object.h for definitions.");
661 return;
664 // reset console animate flags on any previously spawned objects
665 Object *o;
666 FOREACH_OBJECT(o)
668 o->nxflags &= ~NXFLAG_CONSOLE_ANIMATE;
671 // get starting spawn position and spacing
672 int x = player->x + ((player->dir==RIGHT) ? (24 << CSF) : -(24 << CSF));
673 int y = player->y - (16 << CSF);
674 int w = (sprites[objprop[type].sprite].w + 4) << CSF;
676 // create 'em
677 for(i=0;i<count;i++)
679 Object *o = CreateObject(x, y, type);
681 o->dir = player->dir;
682 o->nxflags |= NXFLAG_CONSOLE_ANIMATE;
683 x += w;
686 if (count != 1)
687 Respond("%s x%d", DescribeObjectType(type), count);
688 else
689 Respond("%s", DescribeObjectType(type));
692 static void __animate(StringList *args, int num)
694 Object *o;
696 if (args->CountItems() == 2)
697 { // specifying explicitly by id2
698 o = FindObjectByID2(atoi(args->StringAt(0)));
699 if (o)
700 o->state = atoi(args->StringAt(1));
701 else
702 Respond("Object not found.");
704 return;
707 // animating implicitly from last spawn command
708 bool found = false;
709 FOREACH_OBJECT(o)
711 if (o->nxflags & NXFLAG_CONSOLE_ANIMATE)
713 o->state = num;
714 found = true;
718 if (!found)
719 Respond("No objects found.");
722 static void __infinitedamage(StringList *args, int num)
724 if (args->CountItems() > 0)
725 game.debug.infinite_damage = num;
726 else
727 game.debug.infinite_damage ^= 1;
729 Respond(game.debug.infinite_damage ? "My, oh my..." : "Back to normal.");
732 static void __killall(StringList *args, int num)
734 for(int i=0;i<nOnscreenObjects;i++)
736 Object *o = onscreen_objects[i];
737 if (o->flags & FLAG_SHOOTABLE)
739 o->flags &= ~FLAG_INVULNERABLE;
740 o->DealDamage(999);
745 static void __movemode(StringList *args, int num)
747 player->movementmode = num;
750 static void __flag(StringList *args, int num)
752 game.flags[num] ^= 1;
753 Respond("Flag %04d: %s", num, game.flags[num] ? "SET":"CLEARED");
756 static void __clearflags(StringList *args, int num)
758 memset(game.flags, 0, sizeof(game.flags));
759 Respond("Warning- all game flags cleared");
762 static void __equip(StringList *args, int num)
764 static const char *equiplist[] =
766 "booster08",
767 "map",
768 "armsbarrier",
769 "turbocharge",
770 "airtank",
771 "booster20",
772 "mimigamask",
773 "whimstar",
774 "nikumaru",
775 NULL
777 int i, mask;
779 const char *item = args->StringAt(0);
780 bool enable = args->StringAt(1) ? atoi(args->StringAt(1)) : true;
782 mask = 0x01;
783 for(i=0;equiplist[i];i++)
785 if (!strcasecmp(equiplist[i], item))
787 // allow only booster 08 or booster 20 at a time
788 if (mask & (EQUIP_BOOSTER08 | EQUIP_BOOSTER20))
789 player->equipmask &= ~(EQUIP_BOOSTER08 | EQUIP_BOOSTER20);
791 if (enable)
792 player->equipmask |= mask;
793 else
794 player->equipmask &= ~mask;
796 Respond("Item %s (0x%04x) %sequipped.",
797 equiplist[i], mask, enable ? "" : "un-");
798 return;
801 mask <<= 1;
804 Respond("Unknown item");
807 static void __giveitem(StringList *args, int num)
809 if (FindInventory(num) == -1)
811 AddInventory(num);
812 Respond("Added item %d to your inventory.", num);
814 else
816 Respond("You already have item %d in your inventory.", num);
820 static void __takeitem(StringList *args, int num)
822 if (FindInventory(num) != -1)
824 DelInventory(num);
825 Respond("Removed item %d from your inventory.", num);
827 else
829 Respond("You don't have item %d in your inventory.", num);
833 static void __qua(StringList *args, int num)
835 if (args->CountItems() > 0)
836 megaquake(50);
837 else
838 quake(50);
841 static void __boa(StringList *args, int num)
843 game.stageboss.SetState(num);
846 // skip to good ending sequence
847 static void __cre(StringList *args, int num)
849 game.reset();
850 game.pause(0);
851 game.setmode(GM_NORMAL);
853 game.flags[1341] = true;
854 game.switchstage.mapno = 70;
855 game.switchstage.playerx = 16;
856 game.switchstage.playery = 16;
857 game.switchstage.eventonentry = 400;
860 static void __reset(StringList *args, int num)
862 game.reset();
865 static void __fps(StringList *args, int num)
867 extern int fps;
869 settings->show_fps ^= 1;
870 settings_save();
871 fps = 0;
875 void c------------------------------() {}
878 static void __set_iquit(StringList *args, int num)
880 settings->instant_quit = num;
881 settings_save();
882 Respond("instant quit: %s", settings->instant_quit ? "enabled":"disabled");
885 static void __set_noquake(StringList *args, int num)
887 settings->no_quake_in_hell = num;
888 settings_save();
889 Respond("no quake in hell: %s", settings->no_quake_in_hell ? "enabled":"disabled");
892 static void __inhibit_fullscreen(StringList *args, int num)
894 settings->inhibit_fullscreen = num;
895 settings_save();
896 Respond("inhibit fullscreen: %s", settings->inhibit_fullscreen ? "enabled":"disabled");
899 static void __emulate_bugs(StringList *args, int num)
901 settings->emulate_bugs = num;
902 settings_save();
903 Respond("emulate bugs: %s", settings->emulate_bugs ? "enabled":"disabled");
906 static void __displayformat(StringList *args, int num)
908 settings->displayformat = num;
909 settings_save();
910 Graphics::FlushAll();
911 Respond("SDL_DisplayFormat: %s", settings->displayformat ? "on":"off");
914 static void __skip_intro(StringList *args, int num)
916 settings->skip_intro = num;
917 settings_save();
918 Respond("skip_intro: %s", settings->skip_intro ? "enabled":"disabled");
922 void c------------------------------() {}
925 static void __hello(StringList *args, int num)
927 Respond("I'm a computer, you ninny. Go get a real friend.");
931 void c------------------------------() {}
934 static void __player_hide(StringList *args, int num)
936 player->hide = num;
939 static void __player_inputs_locked(StringList *args, int num)
941 player->inputs_locked = num;
944 static void __game_frozen(StringList *args, int num)
946 game.frozen = num;
949 static void __textbox_setvisible(StringList *args, int num)
951 textbox.SetVisible(num);