Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef of the old name will be instantiated instead. Override defs/bot.def in your mod and make it use any custom spawnclass you wish. Now games don't have to override `addbot` or `bot_add` inside the multiplayer game rules. There's also more console commands. Clients now have access to: addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
This commit is contained in:
parent
ba4ddbd3f6
commit
4a8f4a6082
18 changed files with 510 additions and 62 deletions
|
@ -109,9 +109,6 @@ MultiplayerRules::ConsoleCommand(NSClientPlayer pp, string cmd)
|
|||
tokenize(cmd);
|
||||
|
||||
switch (argv(0)) {
|
||||
case "bot_add":
|
||||
Bot_AddQuick();
|
||||
break;
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
|
@ -129,4 +126,4 @@ Game_InitRules(void)
|
|||
} else {
|
||||
g_grMode = spawn(MultiplayerRules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
4
platform/base_scripts.pk3dir/def/bot.def
Normal file
4
platform/base_scripts.pk3dir/def/bot.def
Normal file
|
@ -0,0 +1,4 @@
|
|||
entityDef bot
|
||||
{
|
||||
spawnclass NSBot
|
||||
}
|
|
@ -1,5 +1,14 @@
|
|||
// common
|
||||
// bots
|
||||
seta bot_enable 1 // Enable (1) or disable (0) usage of bots in the game.
|
||||
seta bot_pause 0 // Enable (1) or disable (0) an interrupt for the Bot AIs thinking.
|
||||
seta bot_noChat 0 // Enable (1) or disable (0) a suppression of any bot chatter.
|
||||
seta bot_fastChat 0 // Enable (1) or disable (0) bot chatter that does not stop other inputs.
|
||||
seta bot_debug 0 // Enable (1) or disable (0) bot debug features that otherwise won't work.
|
||||
seta bot_developer 0 // Enable (1) or disable (0) bot debug text in console.
|
||||
seta bot_minClients -1 // When set, ensures to fill the server with this many players/bots.
|
||||
|
||||
|
||||
// common
|
||||
seta com_showFPS 0 // Draws the Frames Per Second counter. Has two values: 1 - Simple 2 - Detailed
|
||||
seta com_showTracers 0 // Debug display for tracelines, networking intensive.
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ typedef enum
|
|||
|
||||
/** Base class for the Bot AI.
|
||||
*/
|
||||
class bot:player
|
||||
class NSBot:player
|
||||
{
|
||||
/* routing */
|
||||
int m_iNodes;
|
||||
|
@ -74,7 +74,7 @@ class bot:player
|
|||
float m_flForceWeaponAttack;
|
||||
vector m_vecForceWeaponAttackPos;
|
||||
|
||||
void(void) bot;
|
||||
void(void) NSBot;
|
||||
|
||||
virtual void(botstate_t) SetState;
|
||||
virtual botstate_t(void) GetState;
|
||||
|
@ -107,7 +107,7 @@ entity Bot_AddQuick(void);
|
|||
|
||||
/** Applies random custom colors to the given bot entity. */
|
||||
void
|
||||
Bot_RandomColormap(bot target)
|
||||
Bot_RandomColormap(NSBot target)
|
||||
{
|
||||
vector x = hsv2rgb(random() * 360, 100, 100);
|
||||
float top = x[2] + (x[1] << 8) + (x[0] << 16);
|
||||
|
|
|
@ -15,44 +15,44 @@
|
|||
*/
|
||||
|
||||
botstate_t
|
||||
bot::GetState(void)
|
||||
NSBot::GetState(void)
|
||||
{
|
||||
return m_bsState;
|
||||
}
|
||||
|
||||
void
|
||||
bot::SetState(botstate_t state)
|
||||
NSBot::SetState(botstate_t state)
|
||||
{
|
||||
m_bsState = state;
|
||||
}
|
||||
|
||||
botpersonality_t
|
||||
bot::GetPersonality(void)
|
||||
NSBot::GetPersonality(void)
|
||||
{
|
||||
return m_bpPersonality;
|
||||
}
|
||||
|
||||
float
|
||||
bot::GetWalkSpeed(void)
|
||||
NSBot::GetWalkSpeed(void)
|
||||
{
|
||||
return 120;
|
||||
}
|
||||
|
||||
float
|
||||
bot::GetRunSpeed(void)
|
||||
NSBot::GetRunSpeed(void)
|
||||
{
|
||||
return 240;
|
||||
}
|
||||
|
||||
void
|
||||
bot::RouteClear(void)
|
||||
NSBot::RouteClear(void)
|
||||
{
|
||||
super::RouteClear();
|
||||
m_flNodeGiveup = 0.0f;
|
||||
}
|
||||
|
||||
void
|
||||
bot::BrainThink(int enemyvisible, int enemydistant)
|
||||
NSBot::BrainThink(int enemyvisible, int enemydistant)
|
||||
{
|
||||
/* we had a target and it's now dead. now what? */
|
||||
if (m_eTarget) {
|
||||
|
@ -67,7 +67,7 @@ bot::BrainThink(int enemyvisible, int enemydistant)
|
|||
}
|
||||
|
||||
void
|
||||
bot::UseButton(void)
|
||||
NSBot::UseButton(void)
|
||||
{
|
||||
float bestDist;
|
||||
func_button bestButton = __NULL__;
|
||||
|
@ -96,7 +96,7 @@ bot::UseButton(void)
|
|||
}
|
||||
|
||||
void
|
||||
bot::SeeThink(void)
|
||||
NSBot::SeeThink(void)
|
||||
{
|
||||
NSGameRules rules = (NSGameRules)g_grMode;
|
||||
|
||||
|
@ -163,7 +163,7 @@ bot::SeeThink(void)
|
|||
}
|
||||
|
||||
void
|
||||
bot::CheckRoute(void)
|
||||
NSBot::CheckRoute(void)
|
||||
{
|
||||
float flDist;
|
||||
vector vecEndPos;
|
||||
|
@ -193,7 +193,7 @@ bot::CheckRoute(void)
|
|||
|
||||
/* we're inside the radius */
|
||||
if (flDist <= flRadius) {
|
||||
NSLog("^2bot::^3CheckRoute^7: " \
|
||||
NSLog("^2NSBot::^3CheckRoute^7: " \
|
||||
"%s reached node\n", this.targetname);
|
||||
m_iCurNode--;
|
||||
|
||||
|
@ -218,7 +218,7 @@ bot::CheckRoute(void)
|
|||
|
||||
/* can we walk directly to our target destination? */
|
||||
if (trace_fraction == 1.0) {
|
||||
print("^2bot::^3CheckRoute^7: " \
|
||||
print("^2NSBot::^3CheckRoute^7: " \
|
||||
"Walking directly to last node\n");
|
||||
m_iCurNode = -1;
|
||||
}
|
||||
|
@ -261,13 +261,13 @@ bot::CheckRoute(void)
|
|||
}
|
||||
|
||||
void
|
||||
bot::CreateObjective(void)
|
||||
NSBot::CreateObjective(void)
|
||||
{
|
||||
RouteToPosition(Route_SelectDestination(this));
|
||||
}
|
||||
|
||||
void
|
||||
bot::RunAI(void)
|
||||
NSBot::RunAI(void)
|
||||
{
|
||||
vector aimDir, aimPos;
|
||||
bool enemyVisible, enemyDistant;
|
||||
|
@ -294,7 +294,7 @@ bot::RunAI(void)
|
|||
if (!m_iNodes && autocvar_bot_aimless == 0) {
|
||||
CreateObjective();
|
||||
|
||||
NSLog("bot::RunAI: %s is calculating first bot route",
|
||||
NSLog("NSBot::RunAI: %s is calculating first bot route",
|
||||
this.netname);
|
||||
|
||||
/* our route probably has not been processed yet */
|
||||
|
@ -500,12 +500,12 @@ bot::RunAI(void)
|
|||
}
|
||||
|
||||
void
|
||||
bot::PreFrame(void)
|
||||
NSBot::PreFrame(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
bot::PostFrame(void)
|
||||
NSBot::PostFrame(void)
|
||||
{
|
||||
#ifndef NEW_INVENTORY
|
||||
/* we've picked something new up */
|
||||
|
@ -518,7 +518,7 @@ bot::PostFrame(void)
|
|||
}
|
||||
|
||||
void
|
||||
bot::SetName(string nickname)
|
||||
NSBot::SetName(string nickname)
|
||||
{
|
||||
if (autocvar_bot_prefix)
|
||||
forceinfokey(this, "name", sprintf("%s %s", autocvar_bot_prefix, nickname));
|
||||
|
@ -527,9 +527,66 @@ bot::SetName(string nickname)
|
|||
}
|
||||
|
||||
void
|
||||
bot::bot(void)
|
||||
NSBot::NSBot(void)
|
||||
{
|
||||
classname = "player";
|
||||
targetname = "_nuclide_bot_";
|
||||
forceinfokey(this, "*bot", "1");
|
||||
}
|
||||
|
||||
void
|
||||
Bot_KickRandom(void)
|
||||
{
|
||||
for (entity e = world;(e = find(e, ::classname, "player"));) {
|
||||
if (clienttype(e) == CLIENTTYPE_BOT) {
|
||||
dropclient(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
bot_spawner_think(void)
|
||||
{
|
||||
int clientCount = 0i;
|
||||
int botCount = 0i;
|
||||
int minClientsCvar = (int)cvar("bot_minClients");
|
||||
|
||||
/* if -1, we are not managing _anything_ */
|
||||
if (minClientsCvar == -1) {
|
||||
self.nextthink = time + 5.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
/* count total clients + bots */
|
||||
for (entity e = world;(e = find(e, ::classname, "player"));) {
|
||||
if (clienttype(e) == CLIENTTYPE_BOT) {
|
||||
clientCount++;
|
||||
botCount++;
|
||||
}else if (clienttype(e) == CLIENTTYPE_REAL) {
|
||||
clientCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/* add remove as necessary */
|
||||
if (clientCount < cvar("bot_minClients")) {
|
||||
BotProfile_AddRandom();
|
||||
} else if (clientCount > cvar("bot_minClients")) {
|
||||
if (botCount > 0i) {
|
||||
Bot_KickRandom();
|
||||
}
|
||||
}
|
||||
|
||||
self.nextthink = time + 1.0f;
|
||||
}
|
||||
|
||||
void
|
||||
BotLib_Init(void)
|
||||
{
|
||||
BotProfile_Init();
|
||||
|
||||
/* this spawner manages the active bots */
|
||||
entity bot_spawner = spawn();
|
||||
bot_spawner.think = bot_spawner_think;
|
||||
bot_spawner.nextthink = time + 1.0f;
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
*/
|
||||
|
||||
void
|
||||
bot::ChatSay(string msg)
|
||||
NSBot::ChatSay(string msg)
|
||||
{
|
||||
g_grMode.ChatMessageAll(this, msg);
|
||||
}
|
||||
|
||||
void
|
||||
bot::ChatSayTeam(string msg)
|
||||
NSBot::ChatSayTeam(string msg)
|
||||
{
|
||||
g_grMode.ChatMessageTeam(this, msg);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
void
|
||||
bot::Pain(void)
|
||||
NSBot::Pain(void)
|
||||
{
|
||||
NSGameRules rules = g_grMode;
|
||||
|
||||
|
@ -44,7 +44,7 @@ bot::Pain(void)
|
|||
}
|
||||
|
||||
void
|
||||
bot::SetEnemy(entity en)
|
||||
NSBot::SetEnemy(entity en)
|
||||
{
|
||||
m_eTarget = en;
|
||||
|
||||
|
@ -56,7 +56,7 @@ bot::SetEnemy(entity en)
|
|||
}
|
||||
|
||||
void
|
||||
bot::WeaponThink(void)
|
||||
NSBot::WeaponThink(void)
|
||||
{
|
||||
int r = Weapons_IsEmpty(this, activeweapon);
|
||||
|
||||
|
@ -74,7 +74,7 @@ bot::WeaponThink(void)
|
|||
}
|
||||
|
||||
void
|
||||
bot::WeaponAttack(void)
|
||||
NSBot::WeaponAttack(void)
|
||||
{
|
||||
bool shouldAttack = false;
|
||||
|
||||
|
@ -130,7 +130,7 @@ bot::WeaponAttack(void)
|
|||
}
|
||||
|
||||
void
|
||||
bot::ForceWeaponAttack(vector attackPos, float attackTime)
|
||||
NSBot::ForceWeaponAttack(vector attackPos, float attackTime)
|
||||
{
|
||||
m_vecForceWeaponAttackPos = attackPos;
|
||||
m_flForceWeaponAttack = attackTime + time;
|
||||
|
@ -153,7 +153,7 @@ BotLib_Alert(vector pos, float radius, float t)
|
|||
if (vlen(pos - w.origin) > radius)
|
||||
continue;
|
||||
|
||||
bot f = (bot) w;
|
||||
NSBot f = (NSBot) w;
|
||||
|
||||
/* they already got a target of some kind */
|
||||
if (f.m_eTarget)
|
||||
|
|
|
@ -61,3 +61,30 @@ Bot_AddQuick(void)
|
|||
self = oself;
|
||||
return (newbot);
|
||||
}
|
||||
|
||||
void
|
||||
Bot_KillAllBots(void)
|
||||
{
|
||||
entity oldSelf = self;
|
||||
|
||||
for ( other = world; ( other = find( other, classname, "player" ) ); ) {
|
||||
if ( clienttype( other ) == CLIENTTYPE_BOT ) {
|
||||
self = other;
|
||||
ClientKill();
|
||||
}
|
||||
}
|
||||
|
||||
self = oldSelf;
|
||||
}
|
||||
|
||||
void
|
||||
Bot_ResetAllBotsGoals(void)
|
||||
{
|
||||
for ( other = world; ( other = find( other, classname, "player" ) ); ) {
|
||||
if ( clienttype( other ) == CLIENTTYPE_BOT ) {
|
||||
NSBot theBot = (NSBot)other;
|
||||
theBot.SetEnemy(__NULL__);
|
||||
theBot.RouteClear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "bot.h"
|
||||
#include "botinfo.h"
|
||||
#include "cvar.h"
|
||||
#include "profiles.h"
|
||||
|
||||
|
||||
vector Route_SelectDestination( bot target );
|
||||
vector Route_SelectDestination( NSBot target );
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#includelist
|
||||
defs.h
|
||||
profiles.qc
|
||||
bot.qc
|
||||
bot_chat.qc
|
||||
bot_combat.qc
|
||||
|
|
42
src/botlib/profiles.h
Normal file
42
src/botlib/profiles.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Vera Visions LLC.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* BotScript
|
||||
script/bots.txt
|
||||
|
||||
Listing of various bot profiles
|
||||
where infokeys can be set and interpreted
|
||||
by the game-logic at will.
|
||||
|
||||
The `name` keys has to _always_ be present.
|
||||
The `funname` key is optional.
|
||||
|
||||
Name acts as both an identifier as well
|
||||
as a nickname when `funname` is not present.
|
||||
|
||||
Anything else is considered to be extra.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
string m_strName;
|
||||
string m_strNetName;
|
||||
string m_strExtra;
|
||||
} botScript_t;
|
||||
|
||||
#define BOTSCRIPT_MAX 32
|
||||
botScript_t g_bots[BOTSCRIPT_MAX];
|
||||
var int g_botScriptCount;
|
211
src/botlib/profiles.qc
Normal file
211
src/botlib/profiles.qc
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Vera Visions LLC.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
bool
|
||||
Bot_ExistsInServer(string botName)
|
||||
{
|
||||
for (entity e = world;(e = find(e, ::classname, "player"));) {
|
||||
if (clienttype(e) == CLIENTTYPE_BOT) {
|
||||
if (e.netname == botName) {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
Bot_AddBot_f(string botName)
|
||||
{
|
||||
int extraCount = 0i;
|
||||
int foundID = -1i;
|
||||
entity oldSelf;
|
||||
NSBot newBot;
|
||||
int i = 0i;
|
||||
|
||||
if (!botName) {
|
||||
print("Usage: Addbot <botname> [skill 1-5] [team] [msec delay] [altname]\n");
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (!g_nodes_present) {
|
||||
print("^1BotScript_Add^7: Can't add bot. No waypoints.\n");
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* grab the right profile id */
|
||||
for (i = 0i; i < g_botScriptCount; i++) {
|
||||
if (g_bots[i].m_strName == botName) {
|
||||
foundID = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundID == -1i) {
|
||||
print("^1BotScript_Add^7: Named profile not found.\n");
|
||||
return (false);
|
||||
}
|
||||
|
||||
oldSelf = self;
|
||||
self = spawnclient();
|
||||
|
||||
if (!self) {
|
||||
print("^1BotScript_Add^7: Can't add bot. Server is full\n");
|
||||
self = oldSelf;
|
||||
return (false);
|
||||
}
|
||||
|
||||
newBot = (NSBot)self;
|
||||
newBot.SetInfoKey("name", g_bots[foundID].m_strNetName);
|
||||
|
||||
extraCount = tokenize(g_bots[foundID].m_strExtra);
|
||||
|
||||
for (i = 0i; i < extraCount; i+=2) {
|
||||
newBot.SetInfoKey(argv(i), argv(i+1));
|
||||
}
|
||||
|
||||
ClientConnect();
|
||||
PutClientInServer();
|
||||
|
||||
self = oldSelf;
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool
|
||||
BotProfile_AddRandom(void)
|
||||
{
|
||||
int startValue = (int)floor(random(0, g_botScriptCount));
|
||||
int spawnBot = -1i;
|
||||
|
||||
/* start at a random index */
|
||||
for (int i = startValue; i < g_botScriptCount; i++) {
|
||||
if (Bot_ExistsInServer(g_bots[i].m_strNetName) == false) {
|
||||
spawnBot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* still haven't found it. count down. */
|
||||
if (spawnBot == -1i) {
|
||||
for (int i = startValue - 1i; i > 0i; i--) {
|
||||
if (Bot_ExistsInServer(g_bots[i].m_strNetName) == false) {
|
||||
spawnBot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* every bot exists already */
|
||||
if (spawnBot == -1i) {
|
||||
print("^1BotProfile_AddRandom^7: Not enough profiles available.\n");
|
||||
return (false);
|
||||
}
|
||||
|
||||
Bot_AddBot_f(g_bots[spawnBot].m_strName);
|
||||
return (true);
|
||||
}
|
||||
|
||||
void
|
||||
BotProfile_Init(void)
|
||||
{
|
||||
filestream botScript;
|
||||
string tempString;
|
||||
botScript_t currentDef;
|
||||
int braceDepth = 0i;
|
||||
|
||||
g_botScriptCount = 0i;
|
||||
|
||||
if (autocvar(bot_enable, 1) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
botScript = fopen("scripts/bots.txt", FILE_READ);
|
||||
|
||||
if (botScript < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* line by line */
|
||||
while ((tempString = fgets(botScript))) {
|
||||
int lineSegments = tokenize_console(tempString);
|
||||
|
||||
/* word for word */
|
||||
for (int i = 0i; i < lineSegments; i++) {
|
||||
string word = argv(i);
|
||||
|
||||
switch (word) {
|
||||
case "{":
|
||||
braceDepth++;
|
||||
break;
|
||||
case "}":
|
||||
braceDepth--;
|
||||
|
||||
/* we've reached the end of a definition */
|
||||
if (braceDepth == 0) {
|
||||
/* we have something somewhat valid I guess */
|
||||
if (currentDef.m_strName != "") {
|
||||
g_bots[g_botScriptCount].m_strNetName = currentDef.m_strNetName;
|
||||
g_bots[g_botScriptCount].m_strExtra = currentDef.m_strExtra;
|
||||
|
||||
if (g_bots[g_botScriptCount].m_strNetName == "") {
|
||||
g_bots[g_botScriptCount].m_strNetName = currentDef.m_strName;
|
||||
}
|
||||
|
||||
g_bots[g_botScriptCount].m_strName = strtolower(currentDef.m_strName);
|
||||
|
||||
/* increment the def count */
|
||||
if (g_botScriptCount < BOTSCRIPT_MAX)
|
||||
g_botScriptCount++;
|
||||
}
|
||||
currentDef.m_strName = "";
|
||||
currentDef.m_strNetName = "";
|
||||
currentDef.m_strExtra = "";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (braceDepth == 1) {
|
||||
if (word == "name") {
|
||||
currentDef.m_strName = argv(i+1);
|
||||
i++;
|
||||
} else if (word == "funname") {
|
||||
currentDef.m_strNetName = argv(i+1);
|
||||
i++;
|
||||
} else { /* rest gets dumped into extra */
|
||||
currentDef.m_strExtra = strcat(currentDef.m_strExtra, "\"", word, "\"", " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(botScript);
|
||||
print(sprintf("%i bots parsed\n", g_botScriptCount));
|
||||
}
|
||||
|
||||
void
|
||||
Bot_ListBotProfiles_f(void)
|
||||
{
|
||||
if (!g_botScriptCount) {
|
||||
print("no bot profiles found.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < g_botScriptCount; i++) {
|
||||
print(sprintf("%i: %s\n", i, g_bots[i].m_strName));
|
||||
print(sprintf("\t%S\n", g_bots[i].m_strNetName));
|
||||
print(sprintf("\t%s\n", g_bots[i].m_strExtra));
|
||||
}
|
||||
}
|
|
@ -159,7 +159,7 @@ Route_SelectRandomSpot(void)
|
|||
}
|
||||
|
||||
vector
|
||||
Route_SelectDestination(bot target)
|
||||
Route_SelectDestination(NSBot target)
|
||||
{
|
||||
CGameRules rules;
|
||||
rules = (CGameRules)g_grMode;
|
||||
|
|
|
@ -479,8 +479,8 @@ Way_GoToPoint(entity pl)
|
|||
|
||||
for (entity a = world; (a = find(a, classname, "player"));) {
|
||||
if (clienttype(a) != CLIENTTYPE_REAL) {
|
||||
bot targ;
|
||||
targ = (bot)a;
|
||||
NSBot targ;
|
||||
targ = (NSBot)a;
|
||||
targ.RouteClear();
|
||||
targ.RouteToPosition(pl.origin);
|
||||
print(sprintf("Told bot to go to %v\n", trace_endpos));
|
||||
|
|
|
@ -432,6 +432,47 @@ Cmd_Parse(string sCMD)
|
|||
case "-menu_right":
|
||||
pSeat->m_iInputReload = FALSE;
|
||||
break;
|
||||
/* client aliases for server commands */
|
||||
case "addBot":
|
||||
localcmd(sprintf("sv addBot %s\n", argv(1)));
|
||||
break;
|
||||
case "killAllBots":
|
||||
localcmd(sprintf("sv killAllBots %s\n", argv(1)));
|
||||
break;
|
||||
case "resetAllBotsGoals":
|
||||
localcmd(sprintf("sv resetAllBotsGoals %s\n", argv(1)));
|
||||
break;
|
||||
case "killClass":
|
||||
localcmd(sprintf("sv killClass %s\n", argv(1)));
|
||||
break;
|
||||
case "killMovables":
|
||||
localcmd(sprintf("sv killMovables %s\n", argv(1)));
|
||||
break;
|
||||
case "trigger":
|
||||
localcmd(sprintf("sv trigger %s\n", argv(1)));
|
||||
break;
|
||||
case "input":
|
||||
localcmd(sprintf("sv input %s\n", argv(1)));
|
||||
break;
|
||||
case "listBotProfiles":
|
||||
localcmd(sprintf("sv listBotProfiles %s\n", argv(1)));
|
||||
break;
|
||||
case "listTargets":
|
||||
localcmd(sprintf("sv listTargets %s\n", argv(1)));
|
||||
break;
|
||||
case "teleport":
|
||||
localcmd(sprintf("sv teleport %s\n", argv(1)));
|
||||
break;
|
||||
case "teleportToClass":
|
||||
localcmd(sprintf("sv teleportToClass %s\n", argv(1)));
|
||||
break;
|
||||
case "respawnEntities":
|
||||
localcmd(sprintf("sv respawnEntities %s\n", argv(1)));
|
||||
break;
|
||||
case "spawn":
|
||||
localcmd(sprintf("sv spawn %s\n", argv(1)));
|
||||
break;
|
||||
|
||||
default:
|
||||
return (false);
|
||||
}
|
||||
|
@ -459,6 +500,21 @@ Cmd_Init(void)
|
|||
registercommand("listClientSoundDef");
|
||||
registercommand("listServerSoundDef");
|
||||
|
||||
/* server commands */
|
||||
registercommand("addBot");
|
||||
registercommand("killAllBots");
|
||||
registercommand("resetAllBotsGoals");
|
||||
registercommand("killClass");
|
||||
registercommand("killMovables");
|
||||
registercommand("trigger");
|
||||
registercommand("input");
|
||||
registercommand("listTargets");
|
||||
registercommand("teleport");
|
||||
registercommand("teleportToClass");
|
||||
registercommand("respawnEntities");
|
||||
registercommand("spawn");
|
||||
registercommand("listBotProfiles");
|
||||
|
||||
registercommand("cleardecals");
|
||||
registercommand("testLight");
|
||||
registercommand("testPointLight");
|
||||
|
|
|
@ -493,10 +493,12 @@ EntityDef_SpawnClassname(string className)
|
|||
for (int i = 0i; i < g_entDefCount; i++) {
|
||||
if (className == g_entDefTable[i].entClass) {
|
||||
EntityDef_Precaches(i);
|
||||
NSLog("Spawning eDef %S", className);
|
||||
return EntityDef_PrepareEntity(self, i);
|
||||
}
|
||||
}
|
||||
|
||||
NSLog("^1Failed spawning eDef %S", className);
|
||||
return __NULL__;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,8 @@ ClientConnect(void)
|
|||
/* bot needs special init */
|
||||
#ifdef BOT_INCLUDED
|
||||
if (clienttype(self) == CLIENTTYPE_BOT) {
|
||||
spawnfunc_bot();
|
||||
/* from now on we're of type NSBot */
|
||||
EntityDef_SpawnClassname("bot");
|
||||
} else
|
||||
#endif
|
||||
|
||||
|
@ -182,7 +183,7 @@ PlayerPreThink(void)
|
|||
|
||||
#ifdef BOT_INCLUDED
|
||||
if (clienttype(self) == CLIENTTYPE_BOT) {
|
||||
((bot)self).PreFrame();
|
||||
((NSBot)self).PreFrame();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -208,7 +209,7 @@ PlayerPostThink(void)
|
|||
|
||||
#ifdef BOT_INCLUDED
|
||||
if (clienttype(self) == CLIENTTYPE_BOT) {
|
||||
((bot)self).PostFrame();
|
||||
((NSBot)self).PostFrame();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -458,10 +459,14 @@ initents(void)
|
|||
g_ents_initialized = TRUE;
|
||||
|
||||
/* engine hacks for dedicated servers */
|
||||
cvar_set("s_nominaldistance", "1000");
|
||||
cvar_set("s_nominaldistance", "2048");
|
||||
|
||||
/* other engine hacks */
|
||||
cvar_set("sv_nqplayerphysics", "0");
|
||||
|
||||
#ifdef BOT_INCLUDED
|
||||
BotLib_Init();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Any command executed on the server (either tty, rcon or `sv`) gets
|
||||
|
@ -507,12 +512,40 @@ ConsoleCmd(string cmd)
|
|||
/* time to handle commands that apply to all games */
|
||||
tokenize(cmd);
|
||||
switch (argv(0)) {
|
||||
case "trigger_ent":
|
||||
case "addBot":
|
||||
Bot_AddBot_f(strtolower(argv(1)));
|
||||
break;
|
||||
case "killAllBots":
|
||||
Bot_KillAllBots();
|
||||
break;
|
||||
case "resetAllBotsGoals":
|
||||
Bot_ResetAllBotsGoals();
|
||||
break;
|
||||
case "listBotProfiles":
|
||||
Bot_ListBotProfiles_f();
|
||||
break;
|
||||
case "killClass":
|
||||
string targetClass;
|
||||
targetClass = argv(1);
|
||||
|
||||
if (targetClass)
|
||||
for (entity a = world; (a = find(a, ::classname, targetClass));) {
|
||||
NSEntity t = (NSEntity)a;
|
||||
t.Destroy();
|
||||
}
|
||||
break;
|
||||
case "killMovables":
|
||||
for (entity a = world; (a = findfloat(a, ::movetype, MOVETYPE_PHYSICS));) {
|
||||
NSEntity t = (NSEntity)a;
|
||||
t.Destroy();
|
||||
}
|
||||
break;
|
||||
case "trigger":
|
||||
string targ;
|
||||
targ = argv(1);
|
||||
|
||||
if (targ)
|
||||
for (entity a = world; (a = find(a, ::targetname, argv(1)));) {
|
||||
for (entity a = world; (a = find(a, ::targetname, targ));) {
|
||||
NSEntity t = (NSEntity)a;
|
||||
|
||||
if (t.Trigger)
|
||||
|
@ -530,34 +563,42 @@ ConsoleCmd(string cmd)
|
|||
print(sprintf("Sending input to %d, %S: %S\n", entNum, inputName, inputData));
|
||||
}
|
||||
break;
|
||||
case "goto_ent":
|
||||
case "listTargets":
|
||||
for (entity a = world; (a = findfloat(a, ::identity, 1));) {
|
||||
if (a.targetname) {
|
||||
print(sprintf("%d: %s (%s)\n", num_for_edict(a), a.targetname, a.classname));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "teleport":
|
||||
static entity targetFinder;
|
||||
targetFinder = find(targetFinder, ::targetname, argv(1));
|
||||
|
||||
/* try at least one more time to skip world */
|
||||
if (!targetFinder)
|
||||
targetFinder = find(targetFinder, ::targetname, argv(1));
|
||||
|
||||
if (targetFinder)
|
||||
setorigin(pl, targetFinder.origin);
|
||||
break;
|
||||
case "teleportToClass":
|
||||
static entity finder;
|
||||
finder = find(finder, ::classname, argv(1));
|
||||
|
||||
/* try at least one more time to skip world */
|
||||
if (!finder)
|
||||
finder = find(finder, ::classname, argv(1));
|
||||
|
||||
if (finder)
|
||||
setorigin(pl, finder.origin);
|
||||
break;
|
||||
case "respawn_ents":
|
||||
case "respawnEntities":
|
||||
for (entity a = world; (a = findfloat(a, ::identity, 1));) {
|
||||
NSEntity ent = (NSEntity)a;
|
||||
ent.Respawn();
|
||||
}
|
||||
break;
|
||||
case "spawn":
|
||||
entity eDef = spawn();
|
||||
eDef.classname = strcat("spawnfunc_", argv(1));
|
||||
self = eDef;
|
||||
callfunction(self.classname);
|
||||
self = pl;
|
||||
|
||||
makevectors(pl.v_angle);
|
||||
if (eDef.identity == 1) {
|
||||
NSEntity ent = (NSEntity)eDef;
|
||||
}
|
||||
traceline(pl.origin, pl.origin + (v_forward * 1024), MOVE_NORMAL, pl);
|
||||
setorigin(eDef, trace_endpos);
|
||||
break;
|
||||
case "spawndef":
|
||||
NSEntity unit = EntityDef_CreateClassname(argv(1));
|
||||
makevectors(pl.v_angle);
|
||||
traceline(pl.origin, pl.origin + (v_forward * 1024), MOVE_NORMAL, pl);
|
||||
|
|
|
@ -666,7 +666,7 @@ NSClientPlayer::ServerInputFrame(void)
|
|||
#ifdef BOT_INCLUDED
|
||||
/* wait a few seconds, as we may not have been spawned yet */
|
||||
if (clienttype(this) == CLIENTTYPE_BOT) {
|
||||
((bot)this).RunAI();
|
||||
((NSBot)this).RunAI();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in a new issue