openmohaa/code/fgame/gamecmds.cpp

723 lines
16 KiB
C++
Raw Normal View History

2016-03-27 11:49:47 +02:00
/*
===========================================================================
2023-11-02 20:07:29 +01:00
Copyright (C) 2023 the OpenMoHAA team
2016-03-27 11:49:47 +02:00
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// gamecmds.cpp: Game commands
2023-11-02 20:07:29 +01:00
//
2016-03-27 11:49:47 +02:00
#include "gamecmds.h"
#include "glb_local.h"
#include "camera.h"
#include "viewthing.h"
#include "soundman.h"
#include "navigate.h"
#include "lodthing.h"
#include "player.h"
#include <scriptcompiler.h>
2016-03-27 11:49:47 +02:00
#include "playerbot.h"
2023-01-29 20:59:31 +01:00
#include "consoleevent.h"
2023-08-19 17:58:54 +02:00
#include "g_bot.h"
2016-03-27 11:49:47 +02:00
2023-11-02 20:07:29 +01:00
typedef struct {
const char *command;
qboolean (*func)(gentity_t *ent);
qboolean allclients;
} consolecmd_t;
typedef struct {
const char* prefix;
SafePtr<Listener> master;
} commandmaster_t;
2023-11-02 20:07:29 +01:00
consolecmd_t G_ConsoleCmds[] = {
// command name function available in multiplayer?
{"say", G_SayCmd, qtrue },
{"eventlist", G_EventListCmd, qfalse},
{"pendingevents", G_PendingEventsCmd, qfalse},
{"eventhelp", G_EventHelpCmd, qfalse},
{"dumpevents", G_DumpEventsCmd, qfalse},
{"classevents", G_ClassEventsCmd, qfalse},
{"dumpclassevents", G_DumpClassEventsCmd, qfalse},
{"dumpallclasses", G_DumpAllClassesCmd, qtrue },
{"classlist", G_ClassListCmd, qfalse},
{"classtree", G_ClassTreeCmd, qfalse},
{"cam", G_CameraCmd, qfalse},
{"snd", G_SoundCmd, qfalse},
{"showvar", G_ShowVarCmd, qfalse},
{"levelvars", G_LevelVarsCmd, qfalse},
{"gamevars", G_GameVarsCmd, qfalse},
{"script", G_ScriptCmd, qfalse},
2024-08-31 22:09:36 +02:00
// Added in 2.0
{"reloadmap", G_ReloadMap, qfalse},
2024-08-31 22:09:36 +02:00
// Added in OPM
//====
2023-11-02 20:07:29 +01:00
{"compilescript", G_CompileScript, qfalse},
{"addbot", G_AddBotCommand, qfalse},
{"removebot", G_RemoveBotCommand, qfalse},
2016-03-27 11:49:47 +02:00
#ifdef _DEBUG
2023-11-02 20:07:29 +01:00
{"bot", G_BotCommand, qfalse},
2016-03-27 11:49:47 +02:00
#endif
2024-08-31 22:09:36 +02:00
//====
2023-11-02 20:07:29 +01:00
{NULL, NULL, qfalse}
};
2016-03-27 11:49:47 +02:00
Container<commandmaster_t> commandMasters;
2023-11-02 20:07:29 +01:00
void G_InitConsoleCommands(void)
2016-03-27 11:49:47 +02:00
{
2023-11-02 20:07:29 +01:00
consolecmd_t *cmds;
//
// the game server will interpret these commands, which will be automatically
// forwarded to the server after they are not recognized locally
//
gi.AddCommand("give", NULL);
gi.AddCommand("god", NULL);
gi.AddCommand("notarget", NULL);
gi.AddCommand("noclip", NULL);
gi.AddCommand("kill", NULL);
gi.AddCommand("script", NULL);
gi.AddCommand("ready", NULL);
gi.AddCommand("notready", NULL);
gi.AddCommand("invprev", NULL);
gi.AddCommand("invnext", NULL);
gi.AddCommand("weapprev", NULL);
gi.AddCommand("weapnext", NULL);
gi.AddCommand("reload", NULL);
gi.AddCommand("gameversion", NULL);
gi.AddCommand("fov", NULL);
gi.AddCommand("holster", NULL);
gi.AddCommand("safeholster", NULL);
gi.AddCommand("safezoom", NULL);
gi.AddCommand("zoomoff", NULL);
gi.AddCommand("join_team", NULL);
gi.AddCommand("spectator", NULL);
gi.AddCommand("primarydmweapon", NULL);
gi.AddCommand("secondarydmweapon", NULL);
gi.AddCommand("dmmessage", NULL);
2023-11-02 20:07:29 +01:00
for (cmds = G_ConsoleCmds; cmds->command != NULL; cmds++) {
gi.AddCommand(cmds->command, NULL);
}
2016-03-27 11:49:47 +02:00
}
2023-11-02 20:07:29 +01:00
qboolean G_ConsoleCommand(void)
2016-03-27 11:49:47 +02:00
{
2023-11-02 20:07:29 +01:00
gentity_t *ent;
qboolean result;
consolecmd_t *cmds;
const char *cmd;
result = qfalse;
try {
ent = &g_entities[0];
cmd = gi.Argv(0);
for (cmds = G_ConsoleCmds; cmds->command != NULL; cmds++) {
if (!Q_stricmp(cmd, cmds->command)) {
return cmds->func(ent);
}
}
if (cl_running->integer) {
// Don't execute the command in multiplayer
// otherwise, the command will be executed "by" the first client
result = G_ProcessClientCommand(ent);
}
2023-11-02 20:07:29 +01:00
} catch (const char *error) {
G_ExitWithError(error);
}
return result;
2016-03-27 11:49:47 +02:00
}
2023-11-02 20:07:29 +01:00
void G_ClientCommand(gentity_t *ent)
2016-03-27 11:49:47 +02:00
{
2023-11-02 20:07:29 +01:00
try {
if (ent && !G_ProcessClientCommand(ent)) {
// anything that doesn't match a command will be a chat
//G_Say( ent, false, true );
}
}
catch (const char *error) {
G_ExitWithError(error);
}
2016-03-27 11:49:47 +02:00
}
2023-11-02 20:07:29 +01:00
qboolean G_ProcessClientCommand(gentity_t *ent)
2016-03-27 11:49:47 +02:00
{
2023-11-02 20:07:29 +01:00
const char *cmd;
consolecmd_t *cmds;
int i;
int n;
Player *player;
qboolean allowDev;
2023-11-02 20:07:29 +01:00
if (!ent || !ent->client || !ent->entity) {
// not fully in game yet
return qfalse;
}
// Added in 2.1
// Prevent players from messing with the server with developer commands
allowDev = g_gametype->integer == GT_SINGLE_PLAYER;
2023-11-02 20:07:29 +01:00
cmd = gi.Argv(0);
player = (Player *)ent->entity;
player->m_lastcommand = cmd;
for (cmds = G_ConsoleCmds; cmds->command != NULL; cmds++) {
// if we have multiple clients and this command isn't allowed by multiple clients, skip it
if ((game.maxclients > 1) && (!cmds->allclients)) {
continue;
}
if (!Q_stricmp(cmd, cmds->command)) {
return cmds->func(ent);
}
}
if (Event::Exists(cmd)) {
ConsoleEvent ev(cmd);
ev.SetConsoleEdict(ent);
2023-11-02 20:07:29 +01:00
n = gi.Argc();
for (i = 1; i < n; i++) {
ev.AddToken(gi.Argv(i));
2023-11-02 20:07:29 +01:00
}
if (!Q_stricmpn(cmd, "lod_", 4)) {
if (!allowDev) {
return false;
}
2023-11-02 20:07:29 +01:00
return LODModel.ProcessEvent(ev);
} else if (!Q_stricmpn(cmd, "view", 4)) {
if (!allowDev) {
return false;
}
2023-11-02 20:07:29 +01:00
return Viewmodel.ProcessEvent(ev);
} else {
//
// Added in OPM
//
Listener* master = G_FindMaster(cmd);
if (master) {
return master->ProcessEvent(ev);
}
}
if (ent->entity->CheckEventFlags(&ev)) {
2023-11-02 20:07:29 +01:00
return ent->entity->ProcessEvent(ev);
}
}
return qfalse;
2016-03-27 11:49:47 +02:00
}
/*
==================
G_CreateMaster
==================
*/
void G_CreateMaster(const char* prefix, Listener* master)
{
commandmaster_t commandMaster;
int i;
for (i = 1; i <= commandMasters.NumObjects(); i++) {
const commandmaster_t& commandMaster = commandMasters.ObjectAt(i);
if (!str::icmp(commandMaster.prefix, prefix)) {
return;
}
}
commandMaster.prefix = prefix;
commandMaster.master = master;
commandMasters.AddObject(commandMaster);
}
/*
==================
G_MasterMatches
==================
*/
bool G_MasterMatches(const commandmaster_t& commandMaster, const char* command) {
const char* s1, *s2;
s2 = commandMaster.prefix;
for (s1 = command; *s1 && *s2; s1++, s2++) {
if (tolower(*s1) != tolower(*s2)) {
return false;
}
}
return *s1 == '_';
}
/*
==================
G_FindMaster
==================
*/
Listener* G_FindMaster(const char* command)
{
int i;
for (i = 1; i <= commandMasters.NumObjects(); i++) {
const commandmaster_t& commandMaster = commandMasters.ObjectAt(i);
if (G_MasterMatches(commandMaster, command)) {
return commandMaster.master;
}
}
return NULL;
}
2016-03-27 11:49:47 +02:00
/*
==================
Cmd_Say_f
==================
*/
2023-11-02 20:07:29 +01:00
void G_Say(gentity_t *ent, qboolean team, qboolean arg0)
2016-03-27 11:49:47 +02:00
{
2023-11-02 20:07:29 +01:00
int j;
gentity_t *other;
const char *p;
char text[2048];
if (gi.Argc() < 2 && !arg0) {
return;
}
if (!ent->entity) {
// just in case we're not joined yet.
team = false;
}
if (!DM_FLAG(DF_MODELTEAMS | DF_SKINTEAMS)) {
team = false;
}
if (team) {
Com_sprintf(text, sizeof(text), "(%s): ", ent->client->pers.netname);
} else {
Com_sprintf(text, sizeof(text), "%s: ", ent->client->pers.netname);
}
if (arg0) {
strcat(text, gi.Argv(0));
strcat(text, " ");
strcat(text, gi.Args());
} else {
p = gi.Args();
if (*p == '"') {
p++;
strcat(text, p);
text[strlen(text) - 1] = 0;
} else {
strcat(text, p);
}
}
// don't let text be too long for malicious reasons
if (strlen(text) > 150) {
text[150] = 0;
}
strcat(text, "\n");
if (dedicated->integer) {
gi.SendServerCommand(0, "print \"%s\"", text);
}
for (j = 0; j < game.maxclients; j++) {
other = &g_entities[j];
if (!other->inuse || !other->client || !other->entity) {
continue;
}
gi.SendServerCommand(0, "print \"%s\"", text);
}
2016-03-27 11:49:47 +02:00
}
2023-11-02 20:07:29 +01:00
qboolean G_CameraCmd(gentity_t *ent)
2016-03-27 11:49:47 +02:00
{
2023-11-02 20:07:29 +01:00
Event *ev;
const char *cmd;
int i;
int n;
n = gi.Argc();
if (!n) {
gi.Printf("Usage: cam [command] [arg 1]...[arg n]\n");
return qtrue;
}
cmd = gi.Argv(1);
if (Event::Exists(cmd)) {
2023-11-02 20:07:29 +01:00
ev = new Event(cmd);
for (i = 2; i < n; i++) {
ev->AddToken(gi.Argv(i));
}
CameraMan.ProcessEvent(ev);
} else {
gi.Printf("Unknown camera command '%s'.\n", cmd);
}
return qtrue;
2016-03-27 11:49:47 +02:00
}
2023-11-02 20:07:29 +01:00
qboolean G_SoundCmd(gentity_t *ent)
2016-03-27 11:49:47 +02:00
{
2023-11-02 20:07:29 +01:00
Event *ev;
const char *cmd;
int i;
int n;
n = gi.Argc();
if (!n) {
gi.Printf("Usage: snd [command] [arg 1]...[arg n]\n");
return qtrue;
}
cmd = gi.Argv(1);
if (Event::Exists(cmd)) {
2023-11-02 20:07:29 +01:00
ev = new Event(cmd);
for (i = 2; i < n; i++) {
ev->AddToken(gi.Argv(i));
}
SoundMan.ProcessEvent(ev);
} else {
gi.Printf("Unknown sound command '%s'.\n", cmd);
}
return qtrue;
2016-03-27 11:49:47 +02:00
}
2023-11-02 20:07:29 +01:00
qboolean G_SayCmd(gentity_t *ent)
{
G_Say(ent, false, false);
2016-03-27 11:49:47 +02:00
2023-11-02 20:07:29 +01:00
return qtrue;
}
qboolean G_EventListCmd(gentity_t *ent)
{
const char *mask;
2016-03-27 11:49:47 +02:00
2023-11-02 20:07:29 +01:00
mask = NULL;
if (gi.Argc() > 1) {
mask = gi.Argv(1);
}
2016-03-27 11:49:47 +02:00
2024-08-31 22:09:36 +02:00
Event::ListCommands( mask );
2016-03-27 11:49:47 +02:00
2023-11-02 20:07:29 +01:00
return qtrue;
}
2016-03-27 11:49:47 +02:00
2023-11-02 20:07:29 +01:00
qboolean G_PendingEventsCmd(gentity_t *ent)
2016-03-27 11:49:47 +02:00
{
2023-11-02 20:07:29 +01:00
const char *mask;
mask = NULL;
if (gi.Argc() > 1) {
mask = gi.Argv(1);
}
2024-08-31 22:09:36 +02:00
Event::PendingEvents( mask );
2023-11-02 20:07:29 +01:00
return qtrue;
2016-03-27 11:49:47 +02:00
}
2023-11-02 20:07:29 +01:00
qboolean G_EventHelpCmd(gentity_t *ent)
2016-03-27 11:49:47 +02:00
{
2023-11-02 20:07:29 +01:00
const char *mask;
mask = NULL;
if (gi.Argc() > 1) {
mask = gi.Argv(1);
}
2024-08-31 22:09:36 +02:00
Event::ListDocumentation( mask, false );
2023-11-02 20:07:29 +01:00
return qtrue;
2016-03-27 11:49:47 +02:00
}
2023-11-02 20:07:29 +01:00
qboolean G_DumpEventsCmd(gentity_t *ent)
{
const char *mask;
mask = NULL;
if (gi.Argc() > 1) {
mask = gi.Argv(1);
}
2024-08-31 22:09:36 +02:00
Event::ListDocumentation( mask, true );
2023-11-02 20:07:29 +01:00
return qtrue;
}
qboolean G_ClassEventsCmd(gentity_t *ent)
{
const char *className;
className = NULL;
if (gi.Argc() < 2) {
gi.Printf("Usage: classevents [className]\n");
className = gi.Argv(1);
} else {
className = gi.Argv(1);
2024-08-31 22:09:36 +02:00
ClassEvents( className, qfalse );
2023-11-02 20:07:29 +01:00
}
return qtrue;
}
qboolean G_DumpClassEventsCmd(gentity_t *ent)
{
const char *className;
className = NULL;
if (gi.Argc() < 2) {
gi.Printf("Usage: dumpclassevents [className]\n");
className = gi.Argv(1);
} else {
className = gi.Argv(1);
2024-08-31 22:09:36 +02:00
ClassEvents( className, qtrue );
2023-11-02 20:07:29 +01:00
}
return qtrue;
}
qboolean G_DumpAllClassesCmd(gentity_t *ent)
{
DumpAllClasses();
return qtrue;
}
qboolean G_ClassListCmd(gentity_t *ent)
{
2024-08-31 22:09:36 +02:00
listAllClasses();
2023-11-02 20:07:29 +01:00
return qtrue;
}
qboolean G_ClassTreeCmd(gentity_t *ent)
{
if (gi.Argc() > 1) {
2024-08-31 22:09:36 +02:00
listInheritanceOrder( gi.Argv( 1 ) );
2023-11-02 20:07:29 +01:00
} else {
gi.SendServerCommand(ent - g_entities, "print \"Syntax: classtree [classname].\n\"");
}
return qtrue;
}
qboolean G_ShowVarCmd(gentity_t *ent)
{
return qtrue;
}
void PrintVariableList(ScriptVariableList *list) {}
qboolean G_LevelVarsCmd(gentity_t *ent)
{
gi.Printf("Level Variables\n");
PrintVariableList(level.vars);
return qtrue;
}
qboolean G_GameVarsCmd(gentity_t *ent)
{
gi.Printf("Game Variables\n");
PrintVariableList(game.vars);
return qtrue;
}
2023-11-02 20:07:29 +01:00
qboolean G_ScriptCmd(gentity_t *ent)
{
2023-11-02 20:15:08 +01:00
const char *script;
const char *name;
Entity *scriptEnt;
ConsoleEvent *event;
int numArgs;
int i;
2023-11-02 20:07:29 +01:00
2023-11-02 20:15:08 +01:00
numArgs = gi.Argc();
2023-11-02 20:07:29 +01:00
2023-11-02 20:15:08 +01:00
if (numArgs <= 1) {
gi.Printf("Usage: script [filename]\n");
2023-11-02 20:15:08 +01:00
return qtrue;
}
2023-11-02 20:07:29 +01:00
2023-11-02 20:15:08 +01:00
if (!sv_cheats->integer) {
gi.Printf("command not available\n");
2023-11-02 20:07:29 +01:00
return qtrue;
2023-11-02 20:15:08 +01:00
}
script = gi.Argv(1);
if (*script == '*') {
scriptEnt = static_cast<Entity *>(G_GetEntity(atoi(script + 1)));
2023-11-02 20:07:29 +01:00
} else {
2023-11-02 20:15:08 +01:00
scriptEnt = static_cast<Entity *>(G_FindTarget(0, script));
2023-11-02 20:07:29 +01:00
}
2023-11-02 20:15:08 +01:00
if (!scriptEnt) {
gi.Printf("Could not find entity %s\n", script);
return qtrue;
}
name = gi.Argv(2);
if (!Event::Exists(name)) {
gi.Printf("Unknown command '%s'.\n", name);
return qtrue;
}
event = new ConsoleEvent(name);
event->SetConsoleEdict(ent);
for (i = 3; i < numArgs; i++) {
event->AddToken(gi.Argv(i));
}
return scriptEnt->ProcessEvent(event);
2023-11-02 20:07:29 +01:00
}
2024-08-31 22:09:36 +02:00
qboolean G_ReloadMap(gentity_t* ent)
{
char name[256];
2023-11-02 20:07:29 +01:00
Com_sprintf(name, sizeof(name), "gamemap \"%s\"\n", level.mapname.c_str());
gi.SendConsoleCommand(name);
2023-11-02 20:07:29 +01:00
return qtrue;
}
qboolean G_CompileScript(gentity_t *ent)
{
if (gi.Argc() <= 2) {
gi.Printf("Usage: compilescript [filename] [output file]\n");
return qfalse;
}
CompileAssemble(gi.Argv(1), gi.Argv(2));
return qtrue;
}
qboolean G_AddBotCommand(gentity_t *ent)
{
unsigned int numbots;
unsigned int totalnumbots;
int clientNum = -1;
if (gi.Argc() <= 1) {
gi.Printf("Usage: addbot [numbots] [optional botname]\n");
return qfalse;
}
numbots = atoi(gi.Argv(1));
if (numbots > game.maxclients) {
gi.Printf("addbot must be between 1-%d\n", game.maxclients);
2023-11-02 20:07:29 +01:00
return qfalse;
}
totalnumbots = Q_min(numbots + sv_numbots->integer, game.maxclients);
2023-11-02 20:07:29 +01:00
gi.cvar_set("sv_numbots", va("%d", totalnumbots));
return qtrue;
}
qboolean G_RemoveBotCommand(gentity_t *ent)
2016-03-27 11:49:47 +02:00
{
unsigned int numbots;
2023-08-19 17:58:54 +02:00
unsigned int totalnumbots;
2023-11-02 20:07:29 +01:00
if (gi.Argc() <= 1) {
gi.Printf("Usage: removebot [numbots]\n");
return qfalse;
}
2023-11-02 20:07:29 +01:00
numbots = atoi(gi.Argv(1));
totalnumbots = sv_numbots->integer - Q_min(numbots, sv_numbots->integer);
2016-03-27 11:49:47 +02:00
2023-08-19 17:58:54 +02:00
gi.cvar_set("sv_numbots", va("%d", totalnumbots));
2023-11-02 20:07:29 +01:00
return qtrue;
2016-03-27 11:49:47 +02:00
}
#ifdef _DEBUG
2023-11-02 20:07:29 +01:00
qboolean G_BotCommand(gentity_t *ent)
2016-03-27 11:49:47 +02:00
{
2023-11-02 20:07:29 +01:00
const char *command;
BotController *bot;
2023-11-02 20:07:29 +01:00
if (botManager.getControllerManager().getControllers().NumObjects() < 1) {
2023-11-02 20:07:29 +01:00
gi.Printf("No bot spawned.\n");
return qfalse;
}
if (gi.Argc() <= 1) {
gi.Printf("Usage: bot [cmd] (arg1) (arg2) (arg3) ...\n");
return qfalse;
}
bot = botManager.getControllerManager().getControllers().ObjectAt(1);
2023-11-02 20:07:29 +01:00
command = gi.Argv(1);
if (!Q_stricmp(command, "movehere")) {
bot->GetMovement().MoveTo(ent->entity->origin);
2023-11-02 20:07:29 +01:00
} else if (!Q_stricmp(command, "moveherenear")) {
float rad = 256.0f;
if (gi.Argc() > 2) {
rad = atof(gi.Argv(2));
}
bot->GetMovement().MoveNear(ent->entity->origin, rad);
2023-11-02 20:07:29 +01:00
} else if (!Q_stricmp(command, "avoidhere")) {
float rad = 256.0f;
if (gi.Argc() > 2) {
rad = atof(gi.Argv(2));
}
bot->GetMovement().AvoidPath(ent->entity->origin, rad);
2023-11-02 20:07:29 +01:00
} else if (!Q_stricmp(command, "telehere")) {
bot->getControlledEntity()->setOrigin(ent->s.origin);
2023-11-02 20:07:29 +01:00
}
return qtrue;
2016-03-27 11:49:47 +02:00
}
#endif