3 * ===============================================================
4 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ===============================================================
7 * This file is not open source and may not be copied without explicit
8 * written permission of AlliedModders LLC. This file may not be redistributed
9 * in whole or significant part.
10 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
15 #include "ConCmdManager.h"
16 #include "sm_srvcmds.h"
17 #include "AdminCache.h"
18 #include "sm_stringutil.h"
19 #include "PlayerManager.h"
20 #include "Translator.h"
22 ConCmdManager g_ConCmds;
24 SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
25 SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
33 typedef List<PlCmdInfo> CmdList;
35 void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info);
37 ConCmdManager::ConCmdManager() : m_Strings(1024)
39 m_pCmds = sm_trie_create();
40 m_pCmdGrps = sm_trie_create();
41 m_bServerCfgDone = true;
43 m_pServerCfgFile = NULL;
44 m_pServerCfgFwd = NULL;
48 ConCmdManager::~ConCmdManager()
50 sm_trie_destroy(m_pCmds);
51 sm_trie_destroy(m_pCmdGrps);
54 void ConCmdManager::OnSourceModAllInitialized()
56 g_PluginSys.AddPluginsListener(this);
57 g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
58 SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
60 ConCommandBase *pCmd = icvar->GetCommands();
64 if (pCmd->IsCommand())
66 name = pCmd->GetName();
67 if (strcmp(name, "exec") == 0)
69 m_pExecCmd = (ConCommand *)pCmd;
73 pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
78 m_pServerCfgFile = (ConVar *)icvar->FindVar("servercfgfile");
79 SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pExecCmd, this, &ConCmdManager::OnExecCmd, true);
80 m_pServerCfgFwd = g_Forwards.CreateForward("OnServerCfg", ET_Ignore, 0, NULL);
84 void ConCmdManager::OnSourceModShutdown()
88 SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pExecCmd, this, &ConCmdManager::OnExecCmd, true);
89 g_Forwards.ReleaseForward(m_pServerCfgFwd);
90 m_pServerCfgFwd = NULL;
94 /* All commands should already be removed by the time we're done */
95 SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
96 g_RootMenu.RemoveRootConsoleCommand("cmds", this);
99 void ConCmdManager::OnSourceModLevelChange(const char *mapName)
101 m_bServerCfgDone = false;
104 void ConCmdManager::OnExecCmd()
106 const char *arg = engine->Cmd_Argv(1);
107 const char *cfgfile = "server.cfg";
109 if (m_pServerCfgFile)
111 cfgfile = m_pServerCfgFile->GetString();
114 if (strcmp(arg, cfgfile) == 0)
116 engine->ServerCommand("sm cmds internal 1\n");
120 void ConCmdManager::NotifyExecDone(const char *file)
122 if (file == NULL && !m_bServerCfgDone)
124 /* Server-cfg file */
125 m_bServerCfgDone = true;
126 m_pServerCfgFwd->Execute(NULL);
130 void ConCmdManager::RemoveConCmds(List<CmdHook *> &cmdlist, IPluginContext *pContext)
132 List<CmdHook *>::iterator iter = cmdlist.begin();
135 while (iter != cmdlist.end())
138 if (pHook->pf->GetParentContext() == pContext)
140 delete pHook->pAdmin;
142 iter = cmdlist.erase(iter);
149 void ConCmdManager::OnPluginDestroyed(IPlugin *plugin)
152 List<ConCmdInfo *> removed;
153 if (plugin->GetProperty("CommandList", (void **)&pList, true))
155 IPluginContext *pContext = plugin->GetBaseContext();
156 CmdList::iterator iter;
158 * Only bother if there's an actual command list on this plugin...
160 for (iter=pList->begin();
164 PlCmdInfo &cmd = (*iter);
165 ConCmdInfo *pInfo = cmd.pInfo;
167 /* Has this chain already been fully cleaned/removed? */
168 if (removed.find(pInfo) != removed.end())
173 /* Remove any hooks from us on this command */
174 RemoveConCmds(pInfo->conhooks, pContext);
175 RemoveConCmds(pInfo->srvhooks, pContext);
177 /* See if there are still hooks */
178 if (pInfo->srvhooks.size())
182 if (pInfo->conhooks.size())
187 /* Remove the command, it should be safe now */
189 removed.push_back(pInfo);
195 void CommandCallback()
197 g_ConCmds.InternalDispatch();
200 void ConCmdManager::SetCommandClient(int client)
202 m_CmdClient = client + 1;
205 ResultType ConCmdManager::DispatchClientCommand(int client, ResultType type)
207 const char *cmd = engine->Cmd_Argv(0);
208 int args = engine->Cmd_Argc() - 1;
211 if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
213 cell_t result = type;
214 cell_t tempres = result;
215 List<CmdHook *>::iterator iter;
217 for (iter=pInfo->conhooks.begin();
218 iter!=pInfo->conhooks.end();
222 if (!pHook->pf->IsRunnable())
226 if (pHook->pAdmin && !CheckAccess(client, cmd, pHook->pAdmin))
228 if (result < Pl_Handled)
234 pHook->pf->PushCell(client);
235 pHook->pf->PushCell(args);
236 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
238 if (tempres > result)
242 if (result == Pl_Stop)
248 type = (ResultType)result;
254 void ConCmdManager::InternalDispatch()
257 * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
258 * OR this dispatch. They will NEVER go through both.
260 * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
263 const char *cmd = engine->Cmd_Argv(0);
265 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
270 cell_t result = Pl_Continue;
271 int args = engine->Cmd_Argc() - 1;
273 List<CmdHook *>::iterator iter;
276 /* Execute server-only commands if viable */
277 if (m_CmdClient == 0 && pInfo->srvhooks.size())
279 cell_t tempres = result;
280 for (iter=pInfo->srvhooks.begin();
281 iter!=pInfo->srvhooks.end();
285 if (!pHook->pf->IsRunnable())
289 pHook->pf->PushCell(args);
290 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
292 if (tempres > result)
296 if (result == Pl_Stop)
303 /* Check if there's an early stop */
304 if (result >= Pl_Stop)
306 if (!pInfo->sourceMod)
308 RETURN_META(MRES_SUPERCEDE);
314 /* Execute console commands */
315 if (pInfo->conhooks.size())
317 cell_t tempres = result;
318 for (iter=pInfo->conhooks.begin();
319 iter!=pInfo->conhooks.end();
323 if (!pHook->pf->IsRunnable())
329 && !CheckAccess(m_CmdClient, cmd, pHook->pAdmin))
331 if (result < Pl_Handled)
338 /* On a listen server, sometimes the server host's client index can be set as 0.
339 * So index 1 is passed to the command callback to correct this potential problem.
341 if (m_CmdClient == 0 && !engine->IsDedicatedServer())
343 pHook->pf->PushCell(1);
345 pHook->pf->PushCell(m_CmdClient);
348 pHook->pf->PushCell(args);
349 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
351 if (tempres > result)
355 if (result == Pl_Stop)
363 if (result >= Pl_Handled)
365 if (!pInfo->sourceMod)
367 RETURN_META(MRES_SUPERCEDE);
373 bool ConCmdManager::CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin)
375 FlagBits cmdflags = pAdmin->eflags;
381 /* If running listen server, then client 1 is the server host and should have 'root' access */
382 if (client == 1 && !engine->IsDedicatedServer())
387 CPlayer *player = g_Players.GetPlayerByIndex(client);
393 AdminId adm = player->GetAdminId();
394 if (adm != INVALID_ADMIN_ID)
396 FlagBits bits = g_Admins.GetAdminFlags(adm, Access_Effective);
398 /* root knows all, WHOA */
399 if ((bits & ADMFLAG_ROOT) == ADMFLAG_ROOT)
404 /* See if our other flags match */
405 if ((bits & cmdflags) == cmdflags)
410 /* Check for overrides */
411 unsigned int groups = g_Admins.GetAdminGroupCount(adm);
414 bool override = false;
415 for (unsigned int i=0; i<groups; i++)
417 gid = g_Admins.GetAdminGroup(adm, i, NULL);
418 /* First get group-level override */
419 override = g_Admins.GetGroupCommandOverride(gid, cmd, Override_CommandGroup, &rule);
420 /* Now get the specific command override */
421 if (g_Admins.GetGroupCommandOverride(gid, cmd, Override_Command, &rule))
427 if (rule == Command_Allow)
430 } else if (rule == Command_Deny) {
437 if (player->IsFakeClient()
438 || player->GetEdict() == NULL)
443 /* If we got here, the command failed... */
445 if (g_Translator.CoreTrans(client, buffer, sizeof(buffer), "No Access", NULL, NULL)
448 snprintf(buffer, sizeof(buffer), "You do not have access to this command");
451 char fullbuffer[192];
452 snprintf(fullbuffer, sizeof(fullbuffer), "[SM] %s.\n", buffer);
454 engine->ClientPrintf(player->GetEdict(), fullbuffer);
459 bool ConCmdManager::AddConsoleCommand(IPluginFunction *pFunction,
461 const char *description,
464 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
471 CmdHook *pHook = new CmdHook();
473 pHook->pf = pFunction;
474 if (description && description[0])
476 pHook->helptext.assign(description);
478 pInfo->conhooks.push_back(pHook);
480 /* Add to the plugin */
482 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
483 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
485 pList = new CmdList();
486 pPlugin->SetProperty("CommandList", pList);
490 info.type = Cmd_Console;
492 AddToPlCmdList(pList, info);
497 bool ConCmdManager::AddAdminCommand(IPluginFunction *pFunction,
501 const char *description,
504 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
511 CmdHook *pHook = new CmdHook();
512 AdminCmdInfo *pAdmin = new AdminCmdInfo();
514 pHook->pf = pFunction;
515 if (description && description[0])
517 pHook->helptext.assign(description);
519 pHook->pAdmin = pAdmin;
523 if (!sm_trie_retrieve(m_pCmdGrps, group, (void **)&object))
525 grpid = m_Strings.AddString(group);
526 sm_trie_insert(m_pCmdGrps, group, (void *)grpid);
531 pAdmin->cmdGrpId = grpid;
532 pAdmin->flags = adminflags;
534 /* First get the command group override, if any */
535 bool override = g_Admins.GetCommandOverride(group,
536 Override_CommandGroup,
539 /* Next get the command override, if any */
540 if (g_Admins.GetCommandOverride(name,
547 /* Assign normal flags if there were no overrides */
550 pAdmin->eflags = pAdmin->flags;
553 /* Finally, add the hook */
554 pInfo->conhooks.push_back(pHook);
556 /* Now add to the plugin */
558 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
559 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
561 pList = new CmdList();
562 pPlugin->SetProperty("CommandList", pList);
566 info.type = Cmd_Admin;
568 AddToPlCmdList(pList, info);
573 bool ConCmdManager::AddServerCommand(IPluginFunction *pFunction,
575 const char *description,
579 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
586 CmdHook *pHook = new CmdHook();
588 pHook->pf = pFunction;
589 if (description && description[0])
591 pHook->helptext.assign(description);
594 pInfo->srvhooks.push_back(pHook);
596 /* Add to the plugin */
598 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
599 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
601 pList = new CmdList();
602 pPlugin->SetProperty("CommandList", pList);
606 info.type = Cmd_Server;
608 AddToPlCmdList(pList, info);
613 void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info)
615 CmdList::iterator iter = pList->begin();
616 bool inserted = false;
617 const char *orig = NULL;
619 orig = info.pInfo->pCmd->GetName();
621 /* Insert this into the help list, SORTED alphabetically. */
622 while (iter != pList->end())
624 PlCmdInfo &obj = (*iter);
625 const char *cmd = obj.pInfo->pCmd->GetName();
626 if (strcmp(orig, cmd) < 0)
628 pList->insert(iter, info);
637 pList->push_back(info);
641 void ConCmdManager::AddToCmdList(ConCmdInfo *info)
643 List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
645 bool inserted = false;
646 const char *orig = NULL;
648 orig = info->pCmd->GetName();
650 /* Insert this into the help list, SORTED alphabetically. */
651 while (iter != m_CmdList.end())
653 const char *cmd = NULL;
655 cmd = pInfo->pCmd->GetName();
656 if (strcmp(orig, cmd) < 0)
658 m_CmdList.insert(iter, info);
667 m_CmdList.push_back(info);
671 void ConCmdManager::UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits)
675 if (type == Override_Command)
677 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
682 List<CmdHook *>::iterator iter;
685 for (iter=pInfo->conhooks.begin(); iter!=pInfo->conhooks.end(); iter++)
692 pHook->pAdmin->eflags = bits;
694 pHook->pAdmin->eflags = pHook->pAdmin->flags;
698 } else if (type == Override_CommandGroup) {
700 if (!sm_trie_retrieve(m_pCmdGrps, cmd, &object))
704 int grpid = (int)object;
706 /* This is bad :( loop through all commands */
707 List<ConCmdInfo *>::iterator iter;
709 for (iter=m_CmdList.begin(); iter!=m_CmdList.end(); iter++)
712 for (List<CmdHook *>::iterator citer=pInfo->conhooks.begin();
713 citer!=pInfo->conhooks.end();
717 if (pHook->pAdmin && pHook->pAdmin->cmdGrpId == grpid)
721 pHook->pAdmin->eflags = bits;
723 pHook->pAdmin->eflags = pHook->pAdmin->flags;
731 void ConCmdManager::RemoveConCmd(ConCmdInfo *info)
733 /* Remove console-specific information
734 * This should always be true as of right now
738 /* Remove from the trie */
739 sm_trie_delete(m_pCmds, info->pCmd->GetName());
743 /* Unlink from SourceMM */
744 g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd);
745 /* Delete the command's memory */
746 char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
747 char *new_name = const_cast<char *>(info->pCmd->GetName());
752 /* Remove the external hook */
753 SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
757 /* Remove from list */
758 m_CmdList.remove(info);
763 bool ConCmdManager::LookForSourceModCommand(const char *cmd)
767 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
772 return pInfo->sourceMod && (pInfo->conhooks.size() > 0);
775 ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
778 if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
780 pInfo = new ConCmdInfo();
781 /* Find the commandopan */
782 ConCommandBase *pBase = icvar->GetCommands();
783 ConCommand *pCmd = NULL;
786 if (strcmp(pBase->GetName(), name) == 0)
788 /* Don't want to return convar with same name */
789 if (!pBase->IsCommand())
794 pCmd = (ConCommand *)pBase;
797 pBase = const_cast<ConCommandBase *>(pBase->GetNext());
802 /* Note that we have to duplicate because the source might not be
803 * a static string, and these expect static memory.
809 char *new_name = sm_strdup(name);
810 char *new_help = sm_strdup(description);
811 pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
812 pInfo->sourceMod = true;
814 SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
819 sm_trie_insert(m_pCmds, name, pInfo);
826 void ConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
830 const char *text = engine->Cmd_Argv(2);
832 if (strcmp(text, "internal") == 0)
834 const char *num = engine->Cmd_Argv(3);
837 NotifyExecDone(NULL);
843 CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id);
847 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
851 const sm_plugininfo_t *plinfo = pPlugin->GetPublicInfo();
852 const char *plname = IS_STR_FILLED(plinfo->name) ? plinfo->name : pPlugin->GetFilename();
855 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
857 g_RootMenu.ConsolePrint("[SM] No commands found for: %s", plname);
862 g_RootMenu.ConsolePrint("[SM] No commands found for: %s", plname);
866 CmdList::iterator iter;
867 const char *type = NULL;
870 g_RootMenu.ConsolePrint("[SM] Listing %d commands for: %s", pList->size(), plname);
871 g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
872 for (iter=pList->begin();
876 PlCmdInfo &cmd = (*iter);
877 if (cmd.type == Cmd_Server)
880 } else if (cmd.type == Cmd_Console) {
882 } else if (cmd.type == Cmd_Admin) {
885 name = cmd.pInfo->pCmd->GetName();
886 if (cmd.pHook->helptext.size())
888 help = cmd.pHook->helptext.c_str();
890 help = cmd.pInfo->pCmd->GetHelpText();
892 g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);
898 g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");
901 static int g_yam_state = 0;
902 void _YamState(int state)
906 g_RootMenu.ConsolePrint("Welcome to the SourceMod Text Adventure.");
907 g_RootMenu.ConsolePrint("Type sm_text to see the last message.");
908 g_RootMenu.ConsolePrint("Type sm_text <word> to follow a capital word.");
909 g_RootMenu.ConsolePrint("-------------------------------");
910 g_RootMenu.ConsolePrint("You are at VALVE HEADQUARTERS.");
911 g_RootMenu.ConsolePrint("To your LEFT is BAILOPAN, rearranging the letters to spell");
912 g_RootMenu.ConsolePrint("\"A HARD VEAL QUEST\". FORWARD is the entrance to the building.");
913 g_RootMenu.ConsolePrint("To your RIGHT is your last chance to flee in terror.");
914 } else if (state == 1) {
915 g_RootMenu.ConsolePrint("BAILOPAN tells you that his name his pronounced");
916 g_RootMenu.ConsolePrint("\"bye low pahn,\" not \"bay low pan.\" Do you ");
917 g_RootMenu.ConsolePrint("MOCK him, or NOD quietly?");
918 } else if (state == 2) {
919 g_RootMenu.ConsolePrint("You enter the Valve building. You hear screams coming from within.");
920 g_RootMenu.ConsolePrint("A grotesque figure lumbers up to greet you; it is Gabe Newell.");
921 g_RootMenu.ConsolePrint("\"Welcome,\" he belches, \"to my lair.\"");
922 g_RootMenu.ConsolePrint("Do you SHAKE Gaben's hand, WALK past him, or OFFER a donut?");
923 } else if (state == 3) {
924 g_RootMenu.ConsolePrint("You walk into the break room. Alfred \"Adolf\" Reynolds and");
925 g_RootMenu.ConsolePrint("Yahn \"Yeti\" Bernier are discussing something (you overhear ");
926 g_RootMenu.ConsolePrint("the phrase \"and next Steam update, here's what we should break\").");
927 g_RootMenu.ConsolePrint("Should you DIE in a fire, REPORT a bug, REQUEST a feature, or ");
928 g_RootMenu.ConsolePrint("SPRAY them with butter?");
933 void _IntExt_CallYams()
935 const char *arg = engine->Cmd_Argv(1);
937 /* should be impossible */
938 if (!arg || arg[0] == '\0')
940 _YamState(g_yam_state);
943 if (g_yam_state == 1)
945 if (strcasecmp(arg, "mock") == 0)
947 g_RootMenu.ConsolePrint("You mock BAILOPAN's pronunciation. In a fit of rage, ");
948 g_RootMenu.ConsolePrint("he sticks an INT 3 call into your chest, rendering you broken.");
949 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
951 } else if (strcasecmp(arg, "nod") == 0) {
952 g_RootMenu.ConsolePrint("You nod quietly, and then slowly back away into the Valve headquarters.");
955 g_RootMenu.ConsolePrint("Commands are MOCK and NOD.");
957 } else if (g_yam_state == 3) {
958 if (strcasecmp(arg, "report") == 0)
960 g_RootMenu.ConsolePrint("You report a bug to Alfred and Yeti. Immediately, both fall asleep.");
961 g_RootMenu.ConsolePrint("You decay in the break room for two years while they sleep.");
962 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
964 } else if (strcasecmp(arg, "request") == 0) {
965 g_RootMenu.ConsolePrint("You request a feature to Alfred and Yeti. They both mutter something");
966 g_RootMenu.ConsolePrint("about it being implemented \"soon.\" Then, by accident, someone sends");
967 g_RootMenu.ConsolePrint("a message over \"Friends.\" The entire building catches fire.");
968 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
970 } else if (strcasecmp(arg, "die") == 0) {
971 g_RootMenu.ConsolePrint("For no reason, you suddenly catch fire. Alfred and Yeti find this");
972 g_RootMenu.ConsolePrint("deeply disturbing, and cover your flaming corpse with Episode 2");
973 g_RootMenu.ConsolePrint("advertisements. Coming soon, with Team Fortress 2, and Portal!");
974 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
976 } else if (strcasecmp(arg, "spray") == 0) {
977 g_RootMenu.ConsolePrint("You spray Alfred and Yeti with butter. Like Jack Thompson to an");
978 g_RootMenu.ConsolePrint("ambulance, Gabe Newell instantly appears and devours them both.");
979 g_RootMenu.ConsolePrint("You run away, just in time, as Gabe Newell explodes, registering ");
980 g_RootMenu.ConsolePrint("a 5.6 earthquake. Outside, world peace is achieved.");
981 g_RootMenu.ConsolePrint("YOU HAVE WON.");
984 } else if (g_yam_state == 2) {
985 if (strcasecmp(arg, "shake") == 0)
987 g_RootMenu.ConsolePrint("You shake Gaben's hand. It is a terrifying and disgusting experience.");
988 g_RootMenu.ConsolePrint("However, you survive, and continue on.");
990 } else if (strcasecmp(arg, "offer") == 0) {
991 g_RootMenu.ConsolePrint("You offer Gabe Newell one (1) donut. With a gleam in his eyes, ");
992 g_RootMenu.ConsolePrint("he picks you up and devours you whole.");
993 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
995 } else if (strcasecmp(arg, "walk") == 0) {
996 g_RootMenu.ConsolePrint("You walk past Gabe Newell. He can't keep up with your pace!");
999 g_RootMenu.ConsolePrint("Commands are SHAKE, OFFER, and WALK.");
1001 } else if (g_yam_state == 0) {
1002 if (strcasecmp(arg, "left") == 0)
1005 } else if (strcasecmp(arg, "right") == 0) {
1006 g_RootMenu.ConsolePrint("You run away from the Valve headquarters in sheer terror.");
1007 g_RootMenu.ConsolePrint("While running, you smash into an unknown person, who turns out to be your soul mate.");
1008 g_RootMenu.ConsolePrint("You marry and raise a family of 3 kids.");
1009 g_RootMenu.ConsolePrint("Many years later, you look back, and realize this was your best choice.");
1010 g_RootMenu.ConsolePrint("YOU HAVE WON.");
1012 } else if (strcasecmp(arg, "forward") == 0) {
1014 } else if (arg[0] != '\0') {
1015 g_RootMenu.ConsolePrint("Commands are FORWARD, LEFT, and RIGHT.");
1020 void _IntExt_EnableYams()
1022 static ConCommand *pCmd = NULL;
1025 pCmd = new ConCommand("sm_text", _IntExt_CallYams, "Fountain of Yams Adventure Game", FCVAR_GAMEDLL);
1026 g_RootMenu.ConsolePrint("Something is now enabled...");
1028 g_RootMenu.ConsolePrint("Something is already enabled...");