1
2 #include "console.h"
3
4 #include "ObjManager.h"
5 #include "game.h"
6 #include "graphics/Renderer.h"
7 #include "nx.h"
8 #include "settings.h"
9 #include "statusbar.h"
10 #include "tsc.h"
11
12 using namespace NXE::Graphics;
13
14 #include "common/misc.h"
15 #include "Utils/Logger.h"
16 #include "debug.h"
17 #include "map.h"
18 #include "p_arms.h"
19 #include "player.h"
20 #include "playerstats.h"
21 #include "sound/SoundManager.h"
22
23 #include <cstdarg>
24 #include <string>
25 #include <vector>
26 #include <SDL.h>
27
28 #define Respond console.Print
29
30 #if defined(_WIN32)
31 #define strcasecmp _stricmp
32 #endif
33
__god(std::vector<std::string> * args,int num)34 static void __god(std::vector<std::string> *args, int num)
35 {
36 game.debug.god = !game.debug.god;
37 Respond("God mode: {}", game.debug.god ? "enabled" : "disabled");
38 }
39
__script(std::vector<std::string> * args,int num)40 static void __script(std::vector<std::string> *args, int num)
41 {
42 // release any focus a current script may have on us
43 if (player->movementmode == MOVEMODE_NORMAL)
44 map_focus(NULL);
45
46 if (game.tsc->StartScript(num))
47 {
48 Respond("Script {:04d} started.", num);
49 }
50 else
51 {
52 Respond("No such script {:04d}", num);
53 }
54 }
55
__warp(std::vector<std::string> * args,int num)56 static void __warp(std::vector<std::string> *args, int num)
57 {
58 if (num == 0)
59 {
60 std::string stagename;
61 for (unsigned int i = 0; i < args->size(); i++)
62 {
63 if (i != 0)
64 stagename += ' ';
65 stagename += (args->at(i));
66 }
67
68 LOG_DEBUG("Looking for '{}'", stagename.c_str());
69 for (num = 0;; num++)
70 {
71 if (num >= num_stages)
72 {
73 if (!strcasecmp(stagename.c_str(), "village"))
74 {
75 num = 11;
76 }
77 else
78 {
79 Respond("Could determine stage number from your description.");
80 return;
81 }
82
83 break;
84 }
85
86 if (strcasebegin(stages[num].stagename, stagename.c_str()))
87 break;
88 if (strcasebegin(stages[num].filename, stagename.c_str()))
89 break;
90 }
91 }
92
93 game.switchstage.mapno = num;
94 game.switchstage.playerx = 16;
95 game.switchstage.playery = 16;
96 }
97
__sound(std::vector<std::string> * args,int num)98 static void __sound(std::vector<std::string> *args, int num)
99 {
100 NXE::Sound::SoundManager::getInstance()->playSfx((NXE::Sound::SFX)num);
101 console.SetVisible(true); // keep console up
102 }
103
__music(std::vector<std::string> * args,int num)104 static void __music(std::vector<std::string> *args, int num)
105 {
106 /* extern std::vector<std::string> org_names;
107 bool ok = true;
108 std::vector<std::string>::size_type i;
109
110 const char *name = args->at(0).c_str();
111 if (num == 0 && strcmp(name, "0") != 0)
112 {
113 for (i = 1; i < org_names.size(); i++)
114 {
115 if (strcasebegin(org_names[i].c_str(), name))
116 {
117 num = i;
118 break;
119 }
120 }
121
122 if (num == 0)
123 {
124 Respond("Don't know that song.");
125 return;
126 }
127 }
128
129 if (num < 0)
130 ok = false;
131 else if (num >= (int)org_names.size())
132 ok = false;
133
134 if (!ok)
135 {
136 Respond("track out of range");
137 music(0);
138 }
139 else
140 {
141 music(0);
142 music(num);
143 if (num > 0)
144 Respond("%s started", org_names[num].c_str());
145 else
146 Respond("ZERO MUZAK");
147 }*/
148 }
149
__giveweapon(std::vector<std::string> * args,int num)150 static void __giveweapon(std::vector<std::string> *args, int num)
151 {
152 if (num >= 0 && num < WPN_COUNT)
153 {
154 GetWeapon(num, 100);
155 }
156 }
157
__dropweapon(std::vector<std::string> * args,int num)158 static void __dropweapon(std::vector<std::string> *args, int num)
159 {
160 if (args->size() == 0)
161 num = player->curWeapon;
162
163 player->weapons[num].hasWeapon = 0;
164 player->weapons[num].maxammo = 0;
165 player->weapons[num].ammo = 0;
166
167 if (num == player->curWeapon)
168 stat_NextWeapon();
169 }
170
171 // set weapon level
__level(std::vector<std::string> * args,int num)172 static void __level(std::vector<std::string> *args, int num)
173 {
174 num--;
175 if (num < 0)
176 num = 0;
177 if (num > 2)
178 num = 2;
179
180 if (player->weapons[player->curWeapon].xp < 5)
181 player->weapons[player->curWeapon].xp = 5;
182
183 for (int timeout = 0; timeout < 500; timeout++)
184 {
185 if (player->weapons[player->curWeapon].level == num)
186 {
187 return;
188 }
189 else if (player->weapons[player->curWeapon].level < num)
190 {
191 AddXP(1);
192 }
193 else
194 {
195 SubXP(1);
196 }
197 }
198
199 // Respond("Timeout");
200 }
201
__ammo(std::vector<std::string> * args,int num)202 static void __ammo(std::vector<std::string> *args, int num)
203 {
204 player->weapons[player->curWeapon].ammo = num;
205 if (player->weapons[player->curWeapon].ammo > player->weapons[player->curWeapon].maxammo)
206 player->weapons[player->curWeapon].maxammo = player->weapons[player->curWeapon].ammo;
207 }
208
__maxammo(std::vector<std::string> * args,int num)209 static void __maxammo(std::vector<std::string> *args, int num)
210 {
211 player->weapons[player->curWeapon].maxammo = num;
212 if (player->weapons[player->curWeapon].ammo > player->weapons[player->curWeapon].maxammo)
213 player->weapons[player->curWeapon].ammo = player->weapons[player->curWeapon].maxammo;
214 }
215
__hp(std::vector<std::string> * args,int num)216 static void __hp(std::vector<std::string> *args, int num)
217 {
218 player->hp = num;
219 if (player->hp > player->maxHealth)
220 player->maxHealth = player->hp;
221 }
222
__maxhp(std::vector<std::string> * args,int num)223 static void __maxhp(std::vector<std::string> *args, int num)
224 {
225 player->maxHealth = num;
226 if (player->hp > player->maxHealth)
227 {
228 player->hp = player->maxHealth;
229 // PHealthBar.displayed_value = player->hp;
230 }
231 }
232
__xp(std::vector<std::string> * args,int num)233 static void __xp(std::vector<std::string> *args, int num)
234 {
235 player->weapons[player->curWeapon].xp = num;
236 }
237
238 /*
239 static void __spawn(std::vector<std::string> *args, int num)
240 {
241 unsigned int i = 0;
242
243 // if first argument is a number interpret it as a count of
244 // objects to spawn.
245 unsigned int count;
246 if (isdigit(args->at(0).c_str()[0]))
247 {
248 count = num;
249 i++;
250 }
251 else
252 {
253 count = 1;
254 }
255
256 // reconstitute the arguments into the name of the object
257 // to be spawned.
258 std::string objName;
259 unsigned int starti = i;
260 for (;; i++)
261 {
262 if (i >= args->size())
263 break;
264
265 if (i > starti)
266 objName += ' ';
267 objName += (args->at(i));
268 }
269
270 // try and get object type from the provided name
271 int type = ObjectNameToType(objName.c_str());
272 if (type == -1)
273 {
274 Respond("Unknown object. See object.h for definitions.");
275 return;
276 }
277
278 // reset console animate flags on any previously spawned objects
279 Object *o;
280 FOREACH_OBJECT(o)
281 {
282 o->nxflags &= ~NXFLAG_CONSOLE_ANIMATE;
283 }
284
285 // get starting spawn position and spacing
286 int x = player->x + ((player->dir == RIGHT) ? (24 * CSFI) : -(24 * CSFI));
287 int y = player->y - (16 * CSFI);
288 int w = (Renderer::getInstance()->sprites.sprites[objprop[type].sprite].w + 4) * CSFI;
289
290 // create 'em
291 for (i = 0; i < count; i++)
292 {
293 Object *o = CreateObject(x, y, type);
294
295 o->dir = player->dir;
296 o->nxflags |= NXFLAG_CONSOLE_ANIMATE;
297 x += w;
298 }
299
300 if (count != 1)
301 Respond("{} x{}", DescribeObjectType(type), count);
302 else
303 Respond("{}", DescribeObjectType(type));
304 }
305
306 static void __animate(std::vector<std::string> *args, int num)
307 {
308 Object *o;
309
310 if (args->size() == 2)
311 { // specifying explicitly by id2
312 o = FindObjectByID2(atoi(args->at(0).c_str()));
313 if (o)
314 o->state = atoi(args->at(1).c_str());
315 else
316 Respond("Object not found.");
317
318 return;
319 }
320
321 // animating implicitly from last spawn command
322 bool found = false;
323 FOREACH_OBJECT(o)
324 {
325 if (o->nxflags & NXFLAG_CONSOLE_ANIMATE)
326 {
327 o->state = num;
328 found = true;
329 }
330 }
331
332 if (!found)
333 Respond("No objects found.");
334 }
335 */
__infinitedamage(std::vector<std::string> * args,int num)336 static void __infinitedamage(std::vector<std::string> *args, int num)
337 {
338 game.debug.infinite_damage = !game.debug.infinite_damage;
339 Respond(game.debug.infinite_damage ? "My, oh my..." : "Back to normal.");
340 }
341
__killall(std::vector<std::string> * args,int num)342 static void __killall(std::vector<std::string> *args, int num)
343 {
344 for (int i = 0; i < nOnscreenObjects; i++)
345 {
346 Object *o = onscreen_objects[i];
347 if (o->flags & FLAG_SHOOTABLE)
348 {
349 o->flags &= ~FLAG_INVULNERABLE;
350 o->DealDamage(999);
351 }
352 }
353 }
354
__movemode(std::vector<std::string> * args,int num)355 static void __movemode(std::vector<std::string> *args, int num)
356 {
357 player->movementmode = num;
358 }
359
__flag(std::vector<std::string> * args,int num)360 static void __flag(std::vector<std::string> *args, int num)
361 {
362 game.flags[num] ^= 1;
363 Respond("Flag {:04d}: {}", num, game.flags[num] ? "SET" : "CLEARED");
364 }
365
__clearflags(std::vector<std::string> * args,int num)366 static void __clearflags(std::vector<std::string> *args, int num)
367 {
368 memset(game.flags, 0, sizeof(game.flags));
369 Respond("Warning- all game flags cleared");
370 }
371 /*
372 static void __equip(std::vector<std::string> *args, int num)
373 {
374 static const char *equiplist[] = {"booster08", "map", "armsbarrier", "turbocharge", "airtank",
375 "booster20", "mimigamask", "whimstar", "nikumaru", NULL};
376 int i, mask;
377
378 const char *item = args->at(0).c_str();
379 bool enable = args->size() > 1 ? atoi(args->at(1).c_str()) : true;
380
381 mask = 0x01;
382 for (i = 0; equiplist[i]; i++)
383 {
384 if (!strcasecmp(equiplist[i], item))
385 {
386 // allow only booster 08 or booster 20 at a time
387 if (mask & (EQUIP_BOOSTER08 | EQUIP_BOOSTER20))
388 player->equipmask &= ~(EQUIP_BOOSTER08 | EQUIP_BOOSTER20);
389
390 if (enable)
391 player->equipmask |= mask;
392 else
393 player->equipmask &= ~mask;
394
395 Respond("Item {} ({:#04x}) %sequipped.", equiplist[i], mask, enable ? "" : "un-");
396 return;
397 }
398
399 mask <<= 1;
400 }
401
402 Respond("Unknown item");
403 }
404 */
__giveitem(std::vector<std::string> * args,int num)405 static void __giveitem(std::vector<std::string> *args, int num)
406 {
407 if (FindInventory(num) == -1)
408 {
409 AddInventory(num);
410 Respond("Added item {} to your inventory.", num);
411 }
412 else
413 {
414 Respond("You already have item {} in your inventory.", num);
415 }
416 }
417
__takeitem(std::vector<std::string> * args,int num)418 static void __takeitem(std::vector<std::string> *args, int num)
419 {
420 if (FindInventory(num) != -1)
421 {
422 DelInventory(num);
423 Respond("Removed item {} from your inventory.", num);
424 }
425 else
426 {
427 Respond("You don't have item {} in your inventory.", num);
428 }
429 }
430 /*
431 static void __quake(std::vector<std::string> *args, int num)
432 {
433 if (args->size() > 0)
434 megaquake(50);
435 else
436 quake(50);
437 }
438
439 static void __boa(std::vector<std::string> *args, int num)
440 {
441 game.stageboss.SetState(num);
442 }
443 */
444 // skip to normal ending sequence
__ending_normal(std::vector<std::string> * args,int num)445 static void __ending_normal(std::vector<std::string> *args, int num)
446 {
447 game.reset();
448 game.pause(0);
449 game.setmode(GM_NORMAL);
450
451 game.flags[1341] = true;
452 game.switchstage.mapno = 70;
453 game.switchstage.playerx = 16;
454 game.switchstage.playery = 16;
455 game.switchstage.eventonentry = 400;
456 }
457
458 // skip to good ending sequence
__ending_good(std::vector<std::string> * args,int num)459 static void __ending_good(std::vector<std::string> *args, int num)
460 {
461 game.reset();
462 game.pause(0);
463 game.setmode(GM_NORMAL);
464
465 game.flags[1341] = true;
466 game.flags[2000] = true;
467 game.switchstage.mapno = 71;
468 game.switchstage.playerx = 16;
469 game.switchstage.playery = 16;
470 game.switchstage.eventonentry = 120;
471 }
472
473 // skip to credits
__cre(std::vector<std::string> * args,int num)474 static void __cre(std::vector<std::string> *args, int num)
475 {
476 game.reset();
477 game.pause(0);
478 game.setmode(GM_NORMAL);
479
480 game.flags[1341] = true;
481 game.flags[162] = true;
482 game.switchstage.mapno = 0;
483 game.switchstage.playerx = 16;
484 game.switchstage.playery = 16;
485 game.switchstage.eventonentry = 100;
486 }
487
__cre_good(std::vector<std::string> * args,int num)488 static void __cre_good(std::vector<std::string> *args, int num)
489 {
490 game.reset();
491 game.pause(0);
492 game.setmode(GM_NORMAL);
493
494 game.flags[1341] = true;
495 game.flags[162] = true;
496 game.flags[2000] = true;
497 game.switchstage.mapno = 0;
498 game.switchstage.playerx = 16;
499 game.switchstage.playery = 16;
500 game.switchstage.eventonentry = 100;
501 }
502 /*
503 static void __reset(std::vector<std::string> *args, int num)
504 {
505 game.reset();
506 }
507 */
__fps(std::vector<std::string> * args,int num)508 static void __fps(std::vector<std::string> *args, int num)
509 {
510 extern int fps;
511
512 settings->show_fps ^= 1;
513 settings_save();
514 fps = 0;
515 }
516
517 /*
518 void c------------------------------() {}
519 */
520
__skip_intro(std::vector<std::string> * args,int num)521 static void __skip_intro(std::vector<std::string> *args, int num)
522 {
523 settings->skip_intro = !settings->skip_intro;
524 settings_save();
525 Respond("skip_intro: {}", settings->skip_intro ? "enabled" : "disabled");
526 }
527
528 /*
529 void c------------------------------() {}
530 */
531
__help(std::vector<std::string> * args,int num)532 static void __help(std::vector<std::string> *args, int num)
533 {
534 Respond("Available commands:");
535 for (auto& command: console.getCommands())
536 {
537 Respond(command.name + " - " + command.help);
538 }
539 }
540
__quit(std::vector<std::string> * args,int num)541 static void __quit(std::vector<std::string> *args, int num)
542 {
543 Respond("Bye-bye!");
544 lastinputs[ESCKEY] = true;
545 game.running = false;
546 }
547
548 /*
549 void c------------------------------() {}
550 */
551 /*
552 static void __hide_player(std::vector<std::string> *args, int num)
553 {
554 player->hide = num;
555 }
556
557 static void __lock_inputs(std::vector<std::string> *args, int num)
558 {
559 player->inputs_locked = num;
560 }
561
562 static void __freeze_game(std::vector<std::string> *args, int num)
563 {
564 game.frozen = num;
565 }
566
567 static void __show_textbox(std::vector<std::string> *args, int num)
568 {
569 textbox.SetVisible(num);
570 }
571 */
DebugConsole()572 DebugConsole::DebugConsole()
573 {
574 commands = {{"god", __god, 0, 0, "Toggle god-mode" },
575 {"script", __script, 1, 1, "Execute script <num>" },
576 {"warp", __warp, 1, 999, "Warp to level <name|num>" },
577 {"sound", __sound, 1, 1, "Make a sound <num>" },
578 {"music", __music, 1, 1, "Play music <name>" },
579 {"giveweapon", __giveweapon, 1, 1, "Give veapon <num>" },
580 {"dropweapon", __dropweapon, 0, 1, "Drop weapon [num] (or current, without parameters)" },
581
582 {"level", __level, 1, 1, "Set current weapon level to <num>" },
583 {"ammo", __ammo, 1, 1, "Give ammo <num>" },
584 {"maxammo", __maxammo, 1, 1, "Set maximum ammo to <num>" },
585 {"hp", __hp, 1, 1, "Give HP <num>" },
586 {"maxhp", __maxhp, 1, 1, "Set maximum HP to <num>" },
587 {"xp", __xp, 1, 1, "Give XP <num>" },
588 // {"spawn", __spawn, 1, 999, "Spawn object <num>" },
589 // {"animate", __animate, 1, 2, "Set object <id> state to <num>" },
590 {"infinitedamage", __infinitedamage, 0, 0, "Infinite damage" },
591 {"killall", __killall, 0, 0, "Kill all on-screen objects" },
592 {"movemode", __movemode, 1, 1, "Set move mode <num>. 0 - normal, 1 - waterway, 2 - no-clip" },
593 {"flag", __flag, 1, 1, "Toggle game flag <num>" },
594 {"clearflags", __clearflags, 0, 0, "Clear all game flags" },
595
596 {"giveitem", __giveitem, 1, 1, "Give item <num>" },
597 {"takeitem", __takeitem, 1, 1, "Remove item <num>" },
598 // {"boss_state", __boa, 1, 1, "Set stageboss state to <num>" },
599 {"ending_normal", __ending_normal, 0, 0, "Run normal ending" },
600 {"ending_good", __ending_good, 0, 0, "Run good ending" },
601 {"credits_normal", __cre, 0, 0, "Run credits(normal)" },
602 {"credits_good", __cre_good, 0, 0, "Run credits(good)" },
603 {"fps", __fps, 0, 0, "Toggle fps display" },
604 {"skip-intro", __skip_intro, 0, 0, "Toggle intro skip" },
605 {"quit", __quit, 0, 0, "Quit game" },
606
607 {"help", __help, 0, 0, "Show this help" }
608 };
609 }
610
611 /*
612 void c------------------------------() {}
613 */
614
SetVisible(bool newstate)615 void DebugConsole::SetVisible(bool newstate)
616 {
617 LOG_DEBUG("DebugConsole::SetVisible({})", newstate ? "true" : "false");
618
619 if (fVisible != newstate)
620 {
621 fVisible = newstate;
622 fKeyDown = 0;
623 fRepeatTimer = 0;
624
625 if (newstate)
626 {
627 fLine = "";
628 fBrowsingExpansion = false;
629 fBackIndex = fBackBuffer.size() - 1;
630
631 fResponse.clear();
632 fCursorTimer = 0;
633 }
634 }
635 }
636
IsVisible()637 bool DebugConsole::IsVisible()
638 {
639 return fVisible;
640 }
641
HandleKey(int key)642 bool DebugConsole::HandleKey(int key)
643 {
644 if (!fVisible)
645 return 0;
646 if (key != 9)
647 fBrowsingExpansion = false;
648
649 if (key != fKeyDown)
650 {
651 fKeyDown = key;
652 fRepeatTimer = 25;
653 }
654 else
655 {
656 fRepeatTimer = 1;
657 }
658
659 fCursorTimer = 0;
660
661 switch (key)
662 {
663 case 27:
664 case '`':
665 {
666 SetVisible(false);
667 }
668 break;
669
670 case 13:
671 case 271: // numeric enter
672 {
673 SetVisible(false);
674
675 Execute(fLine);
676 }
677 break;
678
679 case 10:
680 break;
681
682 case 8:
683 {
684 if (fLine.size() > 0)
685 fLine.pop_back();
686 }
687 break;
688
689 case 9: // command completion
690 {
691 ExpandCommand();
692
693 fBrowsingExpansion = true;
694 fExpandIndex++;
695 }
696 break;
697
698 // command backbuffer
699 case SDLK_UP:
700 case SDLK_DOWN:
701 {
702 if (fBackBuffer.size() > 0)
703 {
704 fBackIndex += (key == SDLK_UP) ? -1 : 1;
705 if (fBackIndex < 0)
706 fBackIndex = (fBackBuffer.size() - 1);
707 else
708 fBackIndex %= fBackBuffer.size();
709
710 fLine = fBackBuffer.at(fBackIndex);
711 }
712 }
713 break;
714
715 default:
716 {
717 fLine.append(1,(char)key);
718 }
719 break;
720 }
721
722 return 1;
723 }
724
HandleKeyRelease(int key)725 void DebugConsole::HandleKeyRelease(int key)
726 {
727 if (key == fKeyDown)
728 {
729 fKeyDown = 0;
730 fRepeatTimer = 0;
731 }
732 }
733
734 /*
735 void c------------------------------() {}
736 */
737
Draw()738 void DebugConsole::Draw()
739 {
740 if (fResponse.size() > 0)
741 {
742 int y = 8 + 8 * fResponse.size();
743 for (auto& str: fResponse)
744 {
745 this->DrawDebugText(str, y);
746 y -= 8;
747 }
748
749 if (--fResponseTimer <= 0)
750 {
751 fResponse.erase(fResponse.begin());
752 if (fResponse.size() > 0)
753 {
754 fResponseTimer = 30;
755 }
756 }
757 }
758 else if (fVisible)
759 {
760 // key-repeat
761 if (fKeyDown)
762 {
763 if (--fRepeatTimer < 0)
764 HandleKey(fKeyDown);
765 }
766
767 std::string buffer;
768 buffer = "-> " + fLine + ((fCursorTimer < 20) ? "_" : " ");
769 this->DrawDebugText(buffer);
770
771 if (++fCursorTimer > 30)
772 fCursorTimer = 0;
773 }
774 }
775
DrawDebugText(const std::string & text,int y)776 void DebugConsole::DrawDebugText(const std::string& text, int y)
777 {
778 Renderer::getInstance()->font.drawLTR(4, (Renderer::getInstance()->screenHeight - y), text, 0xFFAAAA, true);
779 }
780
Execute(std::string & line)781 bool DebugConsole::Execute(std::string& line)
782 {
783 LOG_DEBUG("DebugConsole::Execute('{}')", line);
784
785 // record command in backbuffer
786 if (!fBackBuffer.empty())
787 fBackBuffer.erase(fBackBuffer.end());
788 if (fBackBuffer.size() >= CONSOLE_MAX_BACK)
789 fBackBuffer.erase(fBackBuffer.begin());
790
791 trim(line);
792
793 fBackBuffer.push_back(line);
794 fBackBuffer.push_back(std::string(""));
795
796 // split command into arguments
797 std::vector<std::string> args;
798 std::string cmd = SplitCommand(line, args);
799
800 if (cmd.size())
801 {
802 std::vector<CommandEntry> matches;
803 MatchCommand(cmd, matches);
804
805 if (matches.size() == 1)
806 {
807 CommandEntry command = matches.at(0);
808
809 if (args.size() < command.minArgs || args.size() > command.maxArgs)
810 {
811 if (command.minArgs == command.maxArgs)
812 {
813 Print("'{}' requires {} argument{}", command.name, command.minArgs, (command.minArgs == 1) ? "" : "s");
814 }
815 else if (args.size() < command.minArgs)
816 {
817 Print("'{}' requires at least {} argument{}", command.name, command.minArgs,
818 (command.minArgs == 1) ? "" : "s");
819 }
820 else
821 {
822 Print("'{}' requires no more than {} arguments", command.name, command.maxArgs);
823 }
824 }
825 else
826 {
827 void (*handler)(std::vector<std::string> *, int) = command.handler;
828 int num = (args.size() > 0) ? atoi(args.at(0).c_str()) : 0;
829
830 (*handler)(&args, num);
831 return 1;
832 }
833 }
834 else if (matches.size() == 0)
835 {
836 Print("I don't understand");
837 }
838 else
839 {
840 Print("Ambiguous command");
841 }
842 }
843
844 return 0;
845 }
846
MatchCommand(const std::string & cmd,std::vector<CommandEntry> & matches)847 void DebugConsole::MatchCommand(const std::string& cmd, std::vector<CommandEntry>& matches)
848 {
849 for (auto& command: commands)
850 {
851 size_t pos = command.name.find(cmd);
852 if (pos == 0 && pos != std::string::npos)
853 matches.push_back(command);
854 }
855 }
856
857 // split an input line into command and arguments
858 // returns the command portion of the line
859
SplitCommand(const std::string & line_in,std::vector<std::string> & args)860 std::string DebugConsole::SplitCommand(const std::string& line_in, std::vector<std::string>& args)
861 {
862 std::size_t start = 0;
863 std::string first;
864 std::size_t idx = 0;
865
866 std::size_t end = line_in.find_first_of(" \t");
867
868 while (end != std::string::npos) {
869 if (idx == 0)
870 first = line_in.substr(start, end - start);
871 else
872 args.push_back(line_in.substr(start, end - start));
873 start = end + 1;
874 end = line_in.find_first_of(" \t", start);
875 idx++;
876 }
877
878 if (idx == 0)
879 first = line_in.substr(start, end - start);
880 else
881 args.push_back(line_in.substr(start));
882
883 return first;
884 }
885
886 // tab-expand the current command
ExpandCommand()887 void DebugConsole::ExpandCommand()
888 {
889 std::vector<std::string> args;
890 std::vector<CommandEntry> matches;
891
892 if (!fBrowsingExpansion)
893 {
894 trim(fLine);
895 fLineToExpand = fLine;
896 fExpandIndex = 0;
897 }
898
899 std::string cmd = SplitCommand(fLineToExpand, args);
900 if (cmd.size())
901 {
902 MatchCommand(cmd, matches);
903
904 if (matches.size() > 0)
905 {
906 if (fExpandIndex >= matches.size())
907 fExpandIndex = 0;
908
909 CommandEntry command = matches.at(fExpandIndex);
910 std::string newCommand(command.name);
911
912 for (unsigned int i = 0; i < args.size(); i++)
913 {
914 newCommand.append(" ");
915 newCommand.append(args.at(i));
916 }
917
918 if (args.size() < command.minArgs)
919 newCommand.append(" ");
920
921 fLine = newCommand;
922 }
923 }
924
925 if (matches.size() != 1)
926 NXE::Sound::SoundManager::getInstance()->playSfx(NXE::Sound::SFX::SND_TINK);
927 }
928