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;
47 ConCmdManager::~ConCmdManager()
49 sm_trie_destroy(m_pCmds);
50 sm_trie_destroy(m_pCmdGrps);
53 void ConCmdManager::OnSourceModAllInitialized()
55 g_PluginSys.AddPluginsListener(this);
56 g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
57 SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
59 ConCommandBase *pCmd = icvar->GetCommands();
63 && strcmp(pCmd->GetName(), "exec") == 0)
65 m_pExecCmd = (ConCommand *)pCmd;
68 pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
73 m_pServerCfgFile = (ConVar *)icvar->FindVar("servercfgfile");
74 SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pExecCmd, this, &ConCmdManager::OnExecCmd, true);
75 m_pServerCfgFwd = g_Forwards.CreateForward("OnServerCfg", ET_Ignore, 0, NULL);
79 void ConCmdManager::OnSourceModShutdown()
83 SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pExecCmd, this, &ConCmdManager::OnExecCmd, true);
84 g_Forwards.ReleaseForward(m_pServerCfgFwd);
85 m_pServerCfgFwd = NULL;
89 /* All commands should already be removed by the time we're done */
90 SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
91 g_RootMenu.RemoveRootConsoleCommand("cmds", this);
94 void ConCmdManager::OnSourceModPluginsLoaded()
96 m_bServerCfgDone = false;
99 void ConCmdManager::OnExecCmd()
101 const char *arg = engine->Cmd_Argv(1);
102 const char *cfgfile = "server.cfg";
104 if (m_pServerCfgFile)
106 cfgfile = m_pServerCfgFile->GetString();
109 if (strcmp(arg, cfgfile) == 0)
111 engine->ServerCommand("sm cmds internal 1\n");
115 void ConCmdManager::NotifyExecDone(const char *file)
117 if (file == NULL && !m_bServerCfgDone)
119 /* Server-cfg file */
120 m_bServerCfgDone = true;
121 m_pServerCfgFwd->Execute(NULL);
125 void ConCmdManager::RemoveConCmds(List<CmdHook *> &cmdlist, IPluginContext *pContext)
127 List<CmdHook *>::iterator iter = cmdlist.begin();
130 while (iter != cmdlist.end())
133 if (pHook->pf->GetParentContext() == pContext)
135 delete pHook->pAdmin;
137 iter = cmdlist.erase(iter);
144 void ConCmdManager::OnPluginDestroyed(IPlugin *plugin)
147 List<ConCmdInfo *> removed;
148 if (plugin->GetProperty("CommandList", (void **)&pList, true))
150 IPluginContext *pContext = plugin->GetBaseContext();
151 CmdList::iterator iter;
153 * Only bother if there's an actual command list on this plugin...
155 for (iter=pList->begin();
159 PlCmdInfo &cmd = (*iter);
160 ConCmdInfo *pInfo = cmd.pInfo;
162 /* Has this chain already been fully cleaned/removed? */
163 if (removed.find(pInfo) != removed.end())
168 /* Remove any hooks from us on this command */
169 RemoveConCmds(pInfo->conhooks, pContext);
170 RemoveConCmds(pInfo->srvhooks, pContext);
172 /* See if there are still hooks */
173 if (pInfo->srvhooks.size())
177 if (pInfo->conhooks.size())
182 /* Remove the command, it should be safe now */
184 removed.push_back(pInfo);
190 void CommandCallback()
192 g_ConCmds.InternalDispatch();
195 void ConCmdManager::SetCommandClient(int client)
197 m_CmdClient = client + 1;
200 ResultType ConCmdManager::DispatchClientCommand(int client, ResultType type)
202 const char *cmd = engine->Cmd_Argv(0);
203 int args = engine->Cmd_Argc() - 1;
206 if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
208 cell_t result = type;
209 cell_t tempres = result;
210 List<CmdHook *>::iterator iter;
212 for (iter=pInfo->conhooks.begin();
213 iter!=pInfo->conhooks.end();
217 if (!pHook->pf->IsRunnable())
221 if (pHook->pAdmin && !CheckAccess(client, cmd, pHook->pAdmin))
223 if (result < Pl_Handled)
229 pHook->pf->PushCell(client);
230 pHook->pf->PushCell(args);
231 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
233 if (tempres > result)
237 if (result == Pl_Stop)
243 type = (ResultType)result;
249 void ConCmdManager::InternalDispatch()
252 * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
253 * OR this dispatch. They will NEVER go through both.
255 * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
258 const char *cmd = engine->Cmd_Argv(0);
260 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
265 cell_t result = Pl_Continue;
266 int args = engine->Cmd_Argc() - 1;
268 List<CmdHook *>::iterator iter;
271 /* Execute server-only commands if viable */
272 if (m_CmdClient == 0 && pInfo->srvhooks.size())
274 cell_t tempres = result;
275 for (iter=pInfo->srvhooks.begin();
276 iter!=pInfo->srvhooks.end();
280 if (!pHook->pf->IsRunnable())
284 pHook->pf->PushCell(args);
285 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
287 if (tempres > result)
291 if (result == Pl_Stop)
298 /* Check if there's an early stop */
299 if (result >= Pl_Stop)
301 if (!pInfo->sourceMod)
303 RETURN_META(MRES_SUPERCEDE);
309 /* Execute console commands */
310 if (pInfo->conhooks.size())
312 cell_t tempres = result;
313 for (iter=pInfo->conhooks.begin();
314 iter!=pInfo->conhooks.end();
318 if (!pHook->pf->IsRunnable())
324 && !CheckAccess(m_CmdClient, cmd, pHook->pAdmin))
326 if (result < Pl_Handled)
332 pHook->pf->PushCell(m_CmdClient);
333 pHook->pf->PushCell(args);
334 if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
336 if (tempres > result)
340 if (result == Pl_Stop)
348 if (result >= Pl_Handled)
350 if (!pInfo->sourceMod)
352 RETURN_META(MRES_SUPERCEDE);
358 bool ConCmdManager::CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin)
360 FlagBits cmdflags = pAdmin->eflags;
366 CPlayer *player = g_Players.GetPlayerByIndex(client);
372 AdminId adm = player->GetAdminId();
373 if (adm != INVALID_ADMIN_ID)
375 FlagBits bits = g_Admins.GetAdminFlags(adm, Access_Effective);
377 /* root knows all, WHOA */
378 if ((bits & ADMFLAG_ROOT) == ADMFLAG_ROOT)
383 /* See if our other flags match */
384 if ((bits & cmdflags) == cmdflags)
389 /* Check for overrides */
390 unsigned int groups = g_Admins.GetAdminGroupCount(adm);
393 bool override = false;
394 for (unsigned int i=0; i<groups; i++)
396 gid = g_Admins.GetAdminGroup(adm, i, NULL);
397 /* First get group-level override */
398 override = g_Admins.GetGroupCommandOverride(gid, cmd, Override_CommandGroup, &rule);
399 /* Now get the specific command override */
400 if (g_Admins.GetGroupCommandOverride(gid, cmd, Override_Command, &rule))
406 if (rule == Command_Allow)
409 } else if (rule == Command_Deny) {
416 if (player->IsFakeClient()
417 || player->GetEdict() == NULL)
422 /* If we got here, the command failed... */
424 if (g_Translator.CoreTrans(client, buffer, sizeof(buffer), "No Access", NULL, NULL)
427 snprintf(buffer, sizeof(buffer), "You do not have access to this command");
430 char fullbuffer[192];
431 snprintf(fullbuffer, sizeof(fullbuffer), "[SM] %s.\n", buffer);
433 engine->ClientPrintf(player->GetEdict(), fullbuffer);
438 bool ConCmdManager::AddConsoleCommand(IPluginFunction *pFunction,
440 const char *description,
443 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
450 CmdHook *pHook = new CmdHook();
452 pHook->pf = pFunction;
453 if (description && description[0])
455 pHook->helptext.assign(description);
457 pInfo->conhooks.push_back(pHook);
459 /* Add to the plugin */
461 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
462 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
464 pList = new CmdList();
465 pPlugin->SetProperty("CommandList", pList);
469 info.type = Cmd_Console;
471 AddToPlCmdList(pList, info);
476 bool ConCmdManager::AddAdminCommand(IPluginFunction *pFunction,
480 const char *description,
483 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
490 CmdHook *pHook = new CmdHook();
491 AdminCmdInfo *pAdmin = new AdminCmdInfo();
493 pHook->pf = pFunction;
494 if (description && description[0])
496 pHook->helptext.assign(description);
498 pHook->pAdmin = pAdmin;
502 if (!sm_trie_retrieve(m_pCmdGrps, group, (void **)&object))
504 grpid = m_Strings.AddString(group);
505 sm_trie_insert(m_pCmdGrps, group, (void *)grpid);
510 pAdmin->cmdGrpId = grpid;
511 pAdmin->flags = adminflags;
513 /* First get the command group override, if any */
514 bool override = g_Admins.GetCommandOverride(group,
515 Override_CommandGroup,
518 /* Next get the command override, if any */
519 if (g_Admins.GetCommandOverride(name,
526 /* Assign normal flags if there were no overrides */
529 pAdmin->eflags = pAdmin->flags;
532 /* Finally, add the hook */
533 pInfo->conhooks.push_back(pHook);
535 /* Now add to the plugin */
537 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
538 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
540 pList = new CmdList();
541 pPlugin->SetProperty("CommandList", pList);
545 info.type = Cmd_Admin;
547 AddToPlCmdList(pList, info);
552 bool ConCmdManager::AddServerCommand(IPluginFunction *pFunction,
554 const char *description,
558 ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
565 CmdHook *pHook = new CmdHook();
567 pHook->pf = pFunction;
568 if (description && description[0])
570 pHook->helptext.assign(description);
573 pInfo->srvhooks.push_back(pHook);
575 /* Add to the plugin */
577 IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
578 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
580 pList = new CmdList();
581 pPlugin->SetProperty("CommandList", pList);
585 info.type = Cmd_Server;
587 AddToPlCmdList(pList, info);
592 void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info)
594 CmdList::iterator iter = pList->begin();
595 bool inserted = false;
596 const char *orig = NULL;
598 orig = info.pInfo->pCmd->GetName();
600 /* Insert this into the help list, SORTED alphabetically. */
601 while (iter != pList->end())
603 PlCmdInfo &obj = (*iter);
604 const char *cmd = obj.pInfo->pCmd->GetName();
605 if (strcmp(orig, cmd) < 0)
607 pList->insert(iter, info);
616 pList->push_back(info);
620 void ConCmdManager::AddToCmdList(ConCmdInfo *info)
622 List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
624 bool inserted = false;
625 const char *orig = NULL;
627 orig = info->pCmd->GetName();
629 /* Insert this into the help list, SORTED alphabetically. */
630 while (iter != m_CmdList.end())
632 const char *cmd = NULL;
634 cmd = pInfo->pCmd->GetName();
635 if (strcmp(orig, cmd) < 0)
637 m_CmdList.insert(iter, info);
646 m_CmdList.push_back(info);
650 void ConCmdManager::UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits)
654 if (type == Override_Command)
656 if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
661 List<CmdHook *>::iterator iter;
664 for (iter=pInfo->conhooks.begin(); iter!=pInfo->conhooks.end(); iter++)
671 pHook->pAdmin->eflags = bits;
673 pHook->pAdmin->eflags = pHook->pAdmin->flags;
677 } else if (type == Override_CommandGroup) {
679 if (!sm_trie_retrieve(m_pCmdGrps, cmd, &object))
683 int grpid = (int)object;
685 /* This is bad :( loop through all commands */
686 List<ConCmdInfo *>::iterator iter;
688 for (iter=m_CmdList.begin(); iter!=m_CmdList.end(); iter++)
691 for (List<CmdHook *>::iterator citer=pInfo->conhooks.begin();
692 citer!=pInfo->conhooks.end();
696 if (pHook->pAdmin && pHook->pAdmin->cmdGrpId == grpid)
700 pHook->pAdmin->eflags = bits;
702 pHook->pAdmin->eflags = pHook->pAdmin->flags;
710 void ConCmdManager::RemoveConCmd(ConCmdInfo *info)
712 /* Remove console-specific information
713 * This should always be true as of right now
717 /* Remove from the trie */
718 sm_trie_delete(m_pCmds, info->pCmd->GetName());
722 /* Unlink from SourceMM */
723 g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd);
724 /* Delete the command's memory */
725 char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
726 char *new_name = const_cast<char *>(info->pCmd->GetName());
731 /* Remove the external hook */
732 SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
736 /* Remove from list */
737 m_CmdList.remove(info);
742 ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
745 if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
747 pInfo = new ConCmdInfo();
748 /* Find the commandopan */
749 ConCommandBase *pBase = icvar->GetCommands();
750 ConCommand *pCmd = NULL;
753 if (strcmp(pBase->GetName(), name) == 0)
755 /* Don't want to return convar with same name */
756 if (!pBase->IsCommand())
761 pCmd = (ConCommand *)pBase;
764 pBase = const_cast<ConCommandBase *>(pBase->GetNext());
769 /* Note that we have to duplicate because the source might not be
770 * a static string, and these expect static memory.
776 char *new_name = sm_strdup(name);
777 char *new_help = sm_strdup(description);
778 pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
779 pInfo->sourceMod = true;
781 SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
786 sm_trie_insert(m_pCmds, name, pInfo);
793 void ConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
797 const char *text = engine->Cmd_Argv(2);
799 if (strcmp(text, "internal") == 0)
801 const char *num = engine->Cmd_Argv(3);
804 g_ConCmds.NotifyExecDone(NULL);
809 CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id);
813 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
817 const sm_plugininfo_t *plinfo = pPlugin->GetPublicInfo();
818 const char *plname = IS_STR_FILLED(plinfo->name) ? plinfo->name : pPlugin->GetFilename();
821 if (!pPlugin->GetProperty("CommandList", (void **)&pList))
823 g_RootMenu.ConsolePrint("[SM] No commands found for: %s", plname);
828 g_RootMenu.ConsolePrint("[SM] No commands found for: %s", plname);
832 CmdList::iterator iter;
833 const char *type = NULL;
836 g_RootMenu.ConsolePrint("[SM] Listing %d commands for: %s", pList->size(), plname);
837 g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
838 for (iter=pList->begin();
842 PlCmdInfo &cmd = (*iter);
843 if (cmd.type == Cmd_Server)
846 } else if (cmd.type == Cmd_Console) {
848 } else if (cmd.type == Cmd_Admin) {
851 name = cmd.pInfo->pCmd->GetName();
852 if (cmd.pHook->helptext.size())
854 help = cmd.pHook->helptext.c_str();
856 help = cmd.pInfo->pCmd->GetHelpText();
858 g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);
864 g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");
867 static int g_yam_state = 0;
868 void _YamState(int state)
872 g_RootMenu.ConsolePrint("Welcome to the SourceMod Text Adventure.");
873 g_RootMenu.ConsolePrint("Type sm_text to see the last message.");
874 g_RootMenu.ConsolePrint("Type sm_text <word> to follow a capital word.");
875 g_RootMenu.ConsolePrint("-------------------------------");
876 g_RootMenu.ConsolePrint("You are at VALVE HEADQUARTERS.");
877 g_RootMenu.ConsolePrint("To your LEFT is BAILOPAN, rearranging the letters to spell");
878 g_RootMenu.ConsolePrint("\"A HARD VEAL QUEST\". FORWARD is the entrance to the building.");
879 g_RootMenu.ConsolePrint("To your RIGHT is your last chance to flee in terror.");
880 } else if (state == 1) {
881 g_RootMenu.ConsolePrint("BAILOPAN tells you that his name his pronounced");
882 g_RootMenu.ConsolePrint("\"bye low pahn,\" not \"bay low pan.\" Do you ");
883 g_RootMenu.ConsolePrint("MOCK him, or NOD quietly?");
884 } else if (state == 2) {
885 g_RootMenu.ConsolePrint("You enter the Valve building. You hear screams coming from within.");
886 g_RootMenu.ConsolePrint("A grotesque figure lumbers up to greet you; it is Gabe Newell.");
887 g_RootMenu.ConsolePrint("\"Welcome,\" he belches, \"to my lair.\"");
888 g_RootMenu.ConsolePrint("Do you SHAKE Gaben's hand, WALK past him, or OFFER a donut?");
889 } else if (state == 3) {
890 g_RootMenu.ConsolePrint("You walk into the break room. Alfred \"Adolf\" Reynolds and");
891 g_RootMenu.ConsolePrint("Yahn \"Yeti\" Bernier are discussing something (you overhear ");
892 g_RootMenu.ConsolePrint("the phrase \"and next Steam update, here's what we should break\").");
893 g_RootMenu.ConsolePrint("Should you DIE in a fire, REPORT a bug, REQUEST a feature, or ");
894 g_RootMenu.ConsolePrint("SPRAY them with butter?");
899 void _IntExt_CallYams()
901 const char *arg = engine->Cmd_Argv(1);
903 /* should be impossible */
904 if (!arg || arg[0] == '\0')
906 _YamState(g_yam_state);
909 if (g_yam_state == 1)
911 if (strcasecmp(arg, "mock") == 0)
913 g_RootMenu.ConsolePrint("You mock BAILOPAN's pronounciation. In a fit of rage, ");
914 g_RootMenu.ConsolePrint("he sticks an INT 3 call into your chest, rendering you broken.");
915 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
917 } else if (strcasecmp(arg, "nod") == 0) {
918 g_RootMenu.ConsolePrint("You nod quietly, and then slowly back away into the Valve headquarters.");
921 g_RootMenu.ConsolePrint("Commands are MOCK and NOD.");
923 } else if (g_yam_state == 3) {
924 if (strcasecmp(arg, "report") == 0)
926 g_RootMenu.ConsolePrint("You report a bug to Alfred and Yeti. Immediately, both fall asleep.");
927 g_RootMenu.ConsolePrint("You decay in the break room for two years while they sleep.");
928 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
930 } else if (strcasecmp(arg, "request") == 0) {
931 g_RootMenu.ConsolePrint("You request a feature to Alfred and Yeti. They both mutter something");
932 g_RootMenu.ConsolePrint("about it being implemented \"soon.\" Then, by accident, someone sends");
933 g_RootMenu.ConsolePrint("a message over \"Friends.\" The entire building catches fire.");
934 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
936 } else if (strcasecmp(arg, "die") == 0) {
937 g_RootMenu.ConsolePrint("For no reason, you suddenly catch fire. Alfred and Yeti find this");
938 g_RootMenu.ConsolePrint("deeply disturbing, and cover your flaming corpse with Episode 2");
939 g_RootMenu.ConsolePrint("advertisements. Coming soon, with Team Fortress 2, and Portal!");
940 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
942 } else if (strcasecmp(arg, "spray") == 0) {
943 g_RootMenu.ConsolePrint("You spray Alfred and Yeti with butter. Like Jack Thompson to an");
944 g_RootMenu.ConsolePrint("ambulance, Gabe Newell instantly appears and devours them both.");
945 g_RootMenu.ConsolePrint("You run away, just in time, as Gabe Newell explodes, registering ");
946 g_RootMenu.ConsolePrint("a 5.6 earthquake. Outside, world peace is achieved.");
947 g_RootMenu.ConsolePrint("YOU HAVE WON.");
950 } else if (g_yam_state == 2) {
951 if (strcasecmp(arg, "shake") == 0)
953 g_RootMenu.ConsolePrint("You shake Gaben's hand. It is a terrifying and disgusting experience.");
954 g_RootMenu.ConsolePrint("However, you survive, and continue on.");
956 } else if (strcasecmp(arg, "offer") == 0) {
957 g_RootMenu.ConsolePrint("You offer Gabe Newell one (1) donut. With a gleam in his eyes, ");
958 g_RootMenu.ConsolePrint("he picks you up and devours you whole.");
959 g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER.");
961 } else if (strcasecmp(arg, "walk") == 0) {
962 g_RootMenu.ConsolePrint("You walk past Gabe Newell. He can't keep up with your pace!");
965 g_RootMenu.ConsolePrint("Commands are SHAKE, OFFER, and WALK.");
967 } else if (g_yam_state == 0) {
968 if (strcasecmp(arg, "left") == 0)
971 } else if (strcasecmp(arg, "right") == 0) {
972 g_RootMenu.ConsolePrint("You run away from the Valve headquarters in sheer terror.");
973 g_RootMenu.ConsolePrint("While running, you smash into an unknown person, who turns out to be your soul mate.");
974 g_RootMenu.ConsolePrint("You marry and raise a family of 3 kids.");
975 g_RootMenu.ConsolePrint("Many years later, you look back, and realize this was your best choice.");
976 g_RootMenu.ConsolePrint("YOU HAVE WON.");
978 } else if (strcasecmp(arg, "forward") == 0) {
980 } else if (arg[0] != '\0') {
981 g_RootMenu.ConsolePrint("Commands are FORWARD, LEFT, and RIGHT.");
986 void _IntExt_EnableYams()
988 static ConCommand *pCmd = NULL;
991 pCmd = new ConCommand("sm_text", _IntExt_CallYams, "Fountain of Yams Adventure Game", FCVAR_GAMEDLL);
992 g_RootMenu.ConsolePrint("Something is now enabled...");
994 g_RootMenu.ConsolePrint("Something is already enabled...");