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"
21 #include "HalfLife2.h"
22 #include "ChatTriggers.h"
24 ConCmdManager g_ConCmds;
26 SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
27 SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
35 typedef List<PlCmdInfo> CmdList;
37 void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info);
39 ConCmdManager::ConCmdManager() : m_Strings(1024)
41 m_pCmds = sm_trie_create();
42 m_pCmdGrps = sm_trie_create();
43 m_bServerCfgDone = true;
45 m_pServerCfgFile = NULL;
46 m_pServerCfgFwd = NULL;
50 ConCmdManager::~ConCmdManager()
52 sm_trie_destroy(m_pCmds);
53 sm_trie_destroy(m_pCmdGrps);
56 void ConCmdManager::OnSourceModAllInitialized()
58 g_PluginSys.AddPluginsListener(this);
59 g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
60 SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
62 ConCommandBase *pCmd = icvar->GetCommands();
66 if (pCmd->IsCommand())
68 name = pCmd->GetName();
69 if (strcmp(name, "exec") == 0)
71 m_pExecCmd = (ConCommand *)pCmd;
75 pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
80 m_pServerCfgFile = (ConVar *)icvar->FindVar("servercfgfile");
81 SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pExecCmd, this, &ConCmdManager::OnExecCmd, true);
82 m_pServerCfgFwd = g_Forwards.CreateForward("OnServerCfg", ET_Ignore, 0, NULL);
86 void ConCmdManager::OnSourceModShutdown()
90 SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pExecCmd, this, &ConCmdManager::OnExecCmd, true);
91 g_Forwards.ReleaseForward(m_pServerCfgFwd);
92 m_pServerCfgFwd = NULL;
96 /* All commands should already be removed by the time we're done */
97 SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
98 g_RootMenu.RemoveRootConsoleCommand("cmds", this);
101 void ConCmdManager::OnSourceModLevelChange(const char *mapName)
103 m_bServerCfgDone = false;
106 void ConCmdManager::OnExecCmd()
108 const char *arg = engine->Cmd_Argv(1);
109 const char *cfgfile = "server.cfg";
111 if (m_pServerCfgFile)
113 cfgfile = m_pServerCfgFile->GetString();
116 if (strcmp(arg, cfgfile) == 0)
118 engine->ServerCommand("sm cmds internal 1\n");
122 void ConCmdManager::NotifyExecDone(const char *file)
124 if (file == NULL && !m_bServerCfgDone)
126 /* Server-cfg file */
127 m_bServerCfgDone = true;
128 m_pServerCfgFwd->Execute(NULL);
132 void ConCmdManager::RemoveConCmds(List<CmdHook *> &cmdlist, IPluginContext *pContext)
134 List<CmdHook *>::iterator iter = cmdlist.begin();
137 while (iter != cmdlist.end())
140 if (pHook->pf->GetParentContext() == pContext)
142 delete pHook->pAdmin;
144 iter = cmdlist.erase(iter);
151 void ConCmdManager::OnPluginDestroyed(IPlugin *plugin)
154 List<ConCmdInfo *> removed;
155 if (plugin->GetProperty("CommandList", (void **)&pList, true))
157 IPluginContext *pContext = plugin->GetBaseContext();
158 CmdList::iterator iter;
160 * Only bother if there's an actual command list on this plugin...
162 for (iter=pList->begin();
166 PlCmdInfo &cmd = (*iter);
167 ConCmdInfo *pInfo = cmd.pInfo;
169 /* Has this chain already been fully cleaned/removed? */
170 if (removed.find(pInfo) != removed.end())
175 /* Remove any hooks from us on this command */
176 RemoveConCmds(pInfo->conhooks, pContext);
177 RemoveConCmds(pInfo->srvhooks, pContext);
179 /* See if there are still hooks */
180 if (pInfo->srvhooks.size())
184 if (pInfo->conhooks.size())
189 /* Remove the command, it should be safe now */
191 removed.push_back(pInfo);
197 void CommandCallback()
199 g_ConCmds.InternalDispatch();
202 void ConCmdManager::SetCommandClient(int client)
204 m_CmdClient = client + 1;
207 ResultType ConCmdManager::DispatchClientCommand(int client, ResultType type)
209 const char *cmd = engine->Cmd_Argv(0);
210 int args = engine->Cmd_Argc() - 1;
213 if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
215 cell_t result = type;
216 cell_t tempres = result;
217 List<CmdHook *>::iterator iter;
219 for (iter=pInfo->conhooks.begin();
220 iter!=pInfo->conhooks.end();
224 if (!pHook->pf->IsRunnable())
228 if (pHook->pAdmin && !CheckAccess(client, cmd, pHook->pAdmin))
230 if (result < Pl_Handled)
236 pHook->pf->PushCell(client);
237 pHook->pf->PushCell(args);
238 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
240 if (tempres > result)
244 if (result == Pl_Stop)
250 type = (ResultType)result;
256 void ConCmdManager::InternalDispatch()
259 * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
260 * OR this dispatch. They will NEVER go through both.
262 * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
265 const char *cmd = engine->Cmd_Argv(0);
267 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
272 cell_t result = Pl_Continue;
273 int args = engine->Cmd_Argc() - 1;
275 List<CmdHook *>::iterator iter;
278 /* Execute server-only commands if viable */
279 if (m_CmdClient == 0 && pInfo->srvhooks.size())
281 cell_t tempres = result;
282 for (iter=pInfo->srvhooks.begin();
283 iter!=pInfo->srvhooks.end();
287 if (!pHook->pf->IsRunnable())
291 pHook->pf->PushCell(args);
292 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
294 if (tempres > result)
298 if (result == Pl_Stop)
305 /* Check if there's an early stop */
306 if (result >= Pl_Stop)
308 if (!pInfo->sourceMod)
310 RETURN_META(MRES_SUPERCEDE);
316 /* Execute console commands */
317 if (pInfo->conhooks.size())
319 cell_t tempres = result;
320 for (iter=pInfo->conhooks.begin();
321 iter!=pInfo->conhooks.end();
325 if (!pHook->pf->IsRunnable())
331 && !CheckAccess(m_CmdClient, cmd, pHook->pAdmin))
333 if (result < Pl_Handled)
340 /* On a listen server, sometimes the server host's client index can be set as 0.
341 * So index 1 is passed to the command callback to correct this potential problem.
343 if (m_CmdClient == 0 && !engine->IsDedicatedServer())
345 pHook->pf->PushCell(1);
347 pHook->pf->PushCell(m_CmdClient);
350 pHook->pf->PushCell(args);
351 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
353 if (tempres > result)
357 if (result == Pl_Stop)
365 if (result >= Pl_Handled)
367 if (!pInfo->sourceMod)
369 RETURN_META(MRES_SUPERCEDE);
375 bool ConCmdManager::CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin)
377 FlagBits cmdflags = pAdmin->eflags;
383 /* If running listen server, then client 1 is the server host and should have 'root' access */
384 if (client == 1 && !engine->IsDedicatedServer())
389 CPlayer *player = g_Players.GetPlayerByIndex(client);
395 AdminId adm = player->GetAdminId();
396 if (adm != INVALID_ADMIN_ID)
398 FlagBits bits = g_Admins.GetAdminFlags(adm, Access_Effective);
400 /* root knows all, WHOA */
401 if ((bits & ADMFLAG_ROOT) == ADMFLAG_ROOT)
406 /* See if our other flags match */
407 if ((bits & cmdflags) == cmdflags)
412 /* Check for overrides */
413 unsigned int groups = g_Admins.GetAdminGroupCount(adm);
416 bool override = false;
417 for (unsigned int i=0; i<groups; i++)
419 gid = g_Admins.GetAdminGroup(adm, i, NULL);
420 /* First get group-level override */
421 override = g_Admins.GetGroupCommandOverride(gid, cmd, Override_CommandGroup, &rule);
422 /* Now get the specific command override */
423 if (g_Admins.GetGroupCommandOverride(gid, cmd, Override_Command, &rule))
429 if (rule == Command_Allow)
432 } else if (rule == Command_Deny) {
439 if (player->IsFakeClient()
440 || player->GetEdict() == NULL)
445 /* If we got here, the command failed... */
447 if (g_Translator.CoreTrans(client, buffer, sizeof(buffer), "No Access", NULL, NULL)
450 snprintf(buffer, sizeof(buffer), "You do not have access to this command");
453 unsigned int replyto = g_ChatTriggers.GetReplyTo();
454 if (replyto == SM_REPLY_CONSOLE)
456 char fullbuffer[192];
457 snprintf(fullbuffer, sizeof(fullbuffer), "[SM] %s.\n", buffer);
458 engine->ClientPrintf(player->GetEdict(), fullbuffer);
459 } else if (replyto == SM_REPLY_CHAT) {
460 char fullbuffer[192];
461 snprintf(fullbuffer, sizeof(fullbuffer), "[SM] %s.", buffer);
462 g_HL2.TextMsg(client, HUD_PRINTTALK, fullbuffer);
468 bool ConCmdManager::AddConsoleCommand(IPluginFunction *pFunction,
470 const char *description,
473 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
480 CmdHook *pHook = new CmdHook();
482 pHook->pf = pFunction;
483 if (description && description[0])
485 pHook->helptext.assign(description);
487 pInfo->conhooks.push_back(pHook);
489 /* Add to the plugin */
491 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
492 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
494 pList = new CmdList();
495 pPlugin->SetProperty("CommandList", pList);
499 info.type = Cmd_Console;
501 AddToPlCmdList(pList, info);
506 bool ConCmdManager::AddAdminCommand(IPluginFunction *pFunction,
510 const char *description,
513 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
520 CmdHook *pHook = new CmdHook();
521 AdminCmdInfo *pAdmin = new AdminCmdInfo();
523 pHook->pf = pFunction;
524 if (description && description[0])
526 pHook->helptext.assign(description);
528 pHook->pAdmin = pAdmin;
532 if (!sm_trie_retrieve(m_pCmdGrps, group, (void **)&object))
534 grpid = m_Strings.AddString(group);
535 sm_trie_insert(m_pCmdGrps, group, (void *)grpid);
540 pAdmin->cmdGrpId = grpid;
541 pAdmin->flags = adminflags;
543 /* First get the command group override, if any */
544 bool override = g_Admins.GetCommandOverride(group,
545 Override_CommandGroup,
548 /* Next get the command override, if any */
549 if (g_Admins.GetCommandOverride(name,
556 /* Assign normal flags if there were no overrides */
559 pAdmin->eflags = pAdmin->flags;
562 /* Finally, add the hook */
563 pInfo->conhooks.push_back(pHook);
565 /* Now add to the plugin */
567 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
568 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
570 pList = new CmdList();
571 pPlugin->SetProperty("CommandList", pList);
575 info.type = Cmd_Admin;
577 AddToPlCmdList(pList, info);
582 bool ConCmdManager::AddServerCommand(IPluginFunction *pFunction,
584 const char *description,
588 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
595 CmdHook *pHook = new CmdHook();
597 pHook->pf = pFunction;
598 if (description && description[0])
600 pHook->helptext.assign(description);
603 pInfo->srvhooks.push_back(pHook);
605 /* Add to the plugin */
607 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
608 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
610 pList = new CmdList();
611 pPlugin->SetProperty("CommandList", pList);
615 info.type = Cmd_Server;
617 AddToPlCmdList(pList, info);
622 void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info)
624 CmdList::iterator iter = pList->begin();
625 bool inserted = false;
626 const char *orig = NULL;
628 orig = info.pInfo->pCmd->GetName();
630 /* Insert this into the help list, SORTED alphabetically. */
631 while (iter != pList->end())
633 PlCmdInfo &obj = (*iter);
634 const char *cmd = obj.pInfo->pCmd->GetName();
635 if (strcmp(orig, cmd) < 0)
637 pList->insert(iter, info);
646 pList->push_back(info);
650 void ConCmdManager::AddToCmdList(ConCmdInfo *info)
652 List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
654 bool inserted = false;
655 const char *orig = NULL;
657 orig = info->pCmd->GetName();
659 /* Insert this into the help list, SORTED alphabetically. */
660 while (iter != m_CmdList.end())
662 const char *cmd = NULL;
664 cmd = pInfo->pCmd->GetName();
665 if (strcmp(orig, cmd) < 0)
667 m_CmdList.insert(iter, info);
676 m_CmdList.push_back(info);
680 void ConCmdManager::UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits)
684 if (type == Override_Command)
686 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
691 List<CmdHook *>::iterator iter;
694 for (iter=pInfo->conhooks.begin(); iter!=pInfo->conhooks.end(); iter++)
701 pHook->pAdmin->eflags = bits;
703 pHook->pAdmin->eflags = pHook->pAdmin->flags;
707 } else if (type == Override_CommandGroup) {
709 if (!sm_trie_retrieve(m_pCmdGrps, cmd, &object))
713 int grpid = (int)object;
715 /* This is bad :( loop through all commands */
716 List<ConCmdInfo *>::iterator iter;
718 for (iter=m_CmdList.begin(); iter!=m_CmdList.end(); iter++)
721 for (List<CmdHook *>::iterator citer=pInfo->conhooks.begin();
722 citer!=pInfo->conhooks.end();
726 if (pHook->pAdmin && pHook->pAdmin->cmdGrpId == grpid)
730 pHook->pAdmin->eflags = bits;
732 pHook->pAdmin->eflags = pHook->pAdmin->flags;
740 void ConCmdManager::RemoveConCmd(ConCmdInfo *info)
742 /* Remove console-specific information
743 * This should always be true as of right now
747 /* Remove from the trie */
748 sm_trie_delete(m_pCmds, info->pCmd->GetName());
752 /* Unlink from SourceMM */
753 g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd);
754 /* Delete the command's memory */
755 char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
756 char *new_name = const_cast<char *>(info->pCmd->GetName());
761 /* Remove the external hook */
762 SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
766 /* Remove from list */
767 m_CmdList.remove(info);
772 bool ConCmdManager::LookForSourceModCommand(const char *cmd)
776 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
781 return pInfo->sourceMod && (pInfo->conhooks.size() > 0);
784 ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
787 if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
789 pInfo = new ConCmdInfo();
790 /* Find the commandopan */
791 ConCommandBase *pBase = icvar->GetCommands();
792 ConCommand *pCmd = NULL;
795 if (strcmp(pBase->GetName(), name) == 0)
797 /* Don't want to return convar with same name */
798 if (!pBase->IsCommand())
803 pCmd = (ConCommand *)pBase;
806 pBase = const_cast<ConCommandBase *>(pBase->GetNext());
811 /* Note that we have to duplicate because the source might not be
812 * a static string, and these expect static memory.
818 char *new_name = sm_strdup(name);
819 char *new_help = sm_strdup(description);
820 pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
821 pInfo->sourceMod = true;
823 SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
828 sm_trie_insert(m_pCmds, name, pInfo);
835 void ConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
839 const char *text = engine->Cmd_Argv(2);
841 if (strcmp(text, "internal") == 0)
843 const char *num = engine->Cmd_Argv(3);
846 NotifyExecDone(NULL);
852 CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id);
856 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
860 const sm_plugininfo_t *plinfo = pPlugin->GetPublicInfo();
861 const char *plname = IS_STR_FILLED(plinfo->name) ? plinfo->name : pPlugin->GetFilename();
864 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
866 g_RootMenu.ConsolePrint("[SM] No commands found for: %s", plname);
871 g_RootMenu.ConsolePrint("[SM] No commands found for: %s", plname);
875 CmdList::iterator iter;
876 const char *type = NULL;
879 g_RootMenu.ConsolePrint("[SM] Listing %d commands for: %s", pList->size(), plname);
880 g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
881 for (iter=pList->begin();
885 PlCmdInfo &cmd = (*iter);
886 if (cmd.type == Cmd_Server)
889 } else if (cmd.type == Cmd_Console) {
891 } else if (cmd.type == Cmd_Admin) {
894 name = cmd.pInfo->pCmd->GetName();
895 if (cmd.pHook->helptext.size())
897 help = cmd.pHook->helptext.c_str();
899 help = cmd.pInfo->pCmd->GetHelpText();
901 g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);
907 g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");
910 static int g_yam_state = 0;
911 void _YamState(int state)
915 g_RootMenu.ConsolePrint("Welcome to the SourceMod Text Adventure.");
916 g_RootMenu.ConsolePrint("Type sm_text to see the last message.");
917 g_RootMenu.ConsolePrint("Type sm_text <word> to follow a capital word.");
918 g_RootMenu.ConsolePrint("-------------------------------");
919 g_RootMenu.ConsolePrint("You are at VALVE HEADQUARTERS.");
920 g_RootMenu.ConsolePrint("To your LEFT is BAILOPAN, rearranging the letters to spell");
921 g_RootMenu.ConsolePrint("\"A HARD VEAL QUEST\". FORWARD is the entrance to the building.");
922 g_RootMenu.ConsolePrint("To your RIGHT is your last chance to flee in terror.");
923 } else if (state == 1) {
924 g_RootMenu.ConsolePrint("BAILOPAN tells you that his name his pronounced");
925 g_RootMenu.ConsolePrint("\"bye low pahn,\" not \"bay low pan.\" Do you ");
926 g_RootMenu.ConsolePrint("MOCK him, or NOD quietly?");
927 } else if (state == 2) {
928 g_RootMenu.ConsolePrint("You enter the Valve building. You hear screams coming from within.");
929 g_RootMenu.ConsolePrint("A grotesque figure lumbers up to greet you; it is Gabe Newell.");
930 g_RootMenu.ConsolePrint("\"Welcome,\" he belches, \"to my lair.\"");
931 g_RootMenu.ConsolePrint("Do you SHAKE Gaben's hand, WALK past him, or OFFER a donut?");
932 } else if (state == 3) {
933 g_RootMenu.ConsolePrint("You walk into the break room. Alfred \"Adolf\" Reynolds and");
934 g_RootMenu.ConsolePrint("Yahn \"Yeti\" Bernier are discussing something (you overhear ");
935 g_RootMenu.ConsolePrint("the phrase \"and next Steam update, here's what we should break\").");
936 g_RootMenu.ConsolePrint("Should you DIE in a fire, REPORT a bug, REQUEST a feature, or ");
937 g_RootMenu.ConsolePrint("SPRAY them with butter?");
942 void _IntExt_CallYams()
944 const char *arg = engine->Cmd_Argv(1);
946 /* should be impossible */
947 if (!arg || arg[0] == '\0')
949 _YamState(g_yam_state);
952 if (g_yam_state == 1)
954 if (strcasecmp(arg, "mock") == 0)
956 g_RootMenu.ConsolePrint("You mock BAILOPAN's pronunciation. In a fit of rage, ");
957 g_RootMenu.ConsolePrint("he sticks an INT 3 call into your chest, rendering you broken.");
958 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
960 } else if (strcasecmp(arg, "nod") == 0) {
961 g_RootMenu.ConsolePrint("You nod quietly, and then slowly back away into the Valve headquarters.");
964 g_RootMenu.ConsolePrint("Commands are MOCK and NOD.");
966 } else if (g_yam_state == 3) {
967 if (strcasecmp(arg, "report") == 0)
969 g_RootMenu.ConsolePrint("You report a bug to Alfred and Yeti. Immediately, both fall asleep.");
970 g_RootMenu.ConsolePrint("You decay in the break room for two years while they sleep.");
971 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
973 } else if (strcasecmp(arg, "request") == 0) {
974 g_RootMenu.ConsolePrint("You request a feature to Alfred and Yeti. They both mutter something");
975 g_RootMenu.ConsolePrint("about it being implemented \"soon.\" Then, by accident, someone sends");
976 g_RootMenu.ConsolePrint("a message over \"Friends.\" The entire building catches fire.");
977 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
979 } else if (strcasecmp(arg, "die") == 0) {
980 g_RootMenu.ConsolePrint("For no reason, you suddenly catch fire. Alfred and Yeti find this");
981 g_RootMenu.ConsolePrint("deeply disturbing, and cover your flaming corpse with Episode 2");
982 g_RootMenu.ConsolePrint("advertisements. Coming soon, with Team Fortress 2, and Portal!");
983 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
985 } else if (strcasecmp(arg, "spray") == 0) {
986 g_RootMenu.ConsolePrint("You spray Alfred and Yeti with butter. Like Jack Thompson to an");
987 g_RootMenu.ConsolePrint("ambulance, Gabe Newell instantly appears and devours them both.");
988 g_RootMenu.ConsolePrint("You run away, just in time, as Gabe Newell explodes, registering ");
989 g_RootMenu.ConsolePrint("a 5.6 earthquake. Outside, world peace is achieved.");
990 g_RootMenu.ConsolePrint("YOU HAVE WON.");
993 } else if (g_yam_state == 2) {
994 if (strcasecmp(arg, "shake") == 0)
996 g_RootMenu.ConsolePrint("You shake Gaben's hand. It is a terrifying and disgusting experience.");
997 g_RootMenu.ConsolePrint("However, you survive, and continue on.");
999 } else if (strcasecmp(arg, "offer") == 0) {
1000 g_RootMenu.ConsolePrint("You offer Gabe Newell one (1) donut. With a gleam in his eyes, ");
1001 g_RootMenu.ConsolePrint("he picks you up and devours you whole.");
1002 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
1004 } else if (strcasecmp(arg, "walk") == 0) {
1005 g_RootMenu.ConsolePrint("You walk past Gabe Newell. He can't keep up with your pace!");
1008 g_RootMenu.ConsolePrint("Commands are SHAKE, OFFER, and WALK.");
1010 } else if (g_yam_state == 0) {
1011 if (strcasecmp(arg, "left") == 0)
1014 } else if (strcasecmp(arg, "right") == 0) {
1015 g_RootMenu.ConsolePrint("You run away from the Valve headquarters in sheer terror.");
1016 g_RootMenu.ConsolePrint("While running, you smash into an unknown person, who turns out to be your soul mate.");
1017 g_RootMenu.ConsolePrint("You marry and raise a family of 3 kids.");
1018 g_RootMenu.ConsolePrint("Many years later, you look back, and realize this was your best choice.");
1019 g_RootMenu.ConsolePrint("YOU HAVE WON.");
1021 } else if (strcasecmp(arg, "forward") == 0) {
1023 } else if (arg[0] != '\0') {
1024 g_RootMenu.ConsolePrint("Commands are FORWARD, LEFT, and RIGHT.");
1029 void _IntExt_EnableYams()
1031 static ConCommand *pCmd = NULL;
1034 pCmd = new ConCommand("sm_text", _IntExt_CallYams, "Fountain of Yams Adventure Game", FCVAR_GAMEDLL);
1035 g_RootMenu.ConsolePrint("Something is now enabled...");
1037 g_RootMenu.ConsolePrint("Something is already enabled...");