2017-04-17 11:33:19 +00:00
|
|
|
/*
|
|
|
|
**
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright 1999 Martin Colberg
|
|
|
|
** Copyright 1999-2016 Randy Heit
|
|
|
|
** Copyright 2005-2016 Christoph Oelckers
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
2016-03-01 15:47:10 +00:00
|
|
|
// Cajun bot
|
|
|
|
//
|
|
|
|
// [RH] Moved console commands out of d_netcmd.c (in Cajun source), because
|
|
|
|
// they don't really belong there.
|
|
|
|
|
|
|
|
#include "c_cvars.h"
|
|
|
|
#include "c_dispatch.h"
|
|
|
|
#include "b_bot.h"
|
2019-01-30 23:28:43 +00:00
|
|
|
#include "g_game.h"
|
2016-03-01 15:47:10 +00:00
|
|
|
#include "p_local.h"
|
|
|
|
#include "cmdlib.h"
|
|
|
|
#include "teaminfo.h"
|
|
|
|
#include "d_net.h"
|
2016-09-19 22:41:22 +00:00
|
|
|
#include "serializer.h"
|
2016-03-01 15:47:10 +00:00
|
|
|
#include "d_player.h"
|
2018-11-24 22:48:23 +00:00
|
|
|
#include "w_wad.h"
|
2017-04-12 23:12:04 +00:00
|
|
|
#include "vm.h"
|
2019-01-28 19:15:48 +00:00
|
|
|
#include "g_levellocals.h"
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-11-24 20:36:02 +00:00
|
|
|
IMPLEMENT_CLASS(DBot, false, true)
|
2016-11-05 16:08:54 +00:00
|
|
|
|
|
|
|
IMPLEMENT_POINTERS_START(DBot)
|
|
|
|
IMPLEMENT_POINTER(dest)
|
|
|
|
IMPLEMENT_POINTER(prev)
|
|
|
|
IMPLEMENT_POINTER(enemy)
|
|
|
|
IMPLEMENT_POINTER(missile)
|
|
|
|
IMPLEMENT_POINTER(mate)
|
|
|
|
IMPLEMENT_POINTER(last_mate)
|
|
|
|
IMPLEMENT_POINTERS_END
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2017-01-19 19:56:31 +00:00
|
|
|
DEFINE_FIELD(DBot, dest)
|
|
|
|
|
2019-01-27 12:08:54 +00:00
|
|
|
void DBot::Construct()
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
Clear ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DBot::Clear ()
|
|
|
|
{
|
2019-01-06 08:39:35 +00:00
|
|
|
player = nullptr;
|
2016-03-25 23:34:56 +00:00
|
|
|
Angle = 0.;
|
2019-01-06 08:39:35 +00:00
|
|
|
dest = nullptr;
|
|
|
|
prev = nullptr;
|
|
|
|
enemy = nullptr;
|
|
|
|
missile = nullptr;
|
|
|
|
mate = nullptr;
|
|
|
|
last_mate = nullptr;
|
2016-03-01 15:47:10 +00:00
|
|
|
memset(&skill, 0, sizeof(skill));
|
|
|
|
t_active = 0;
|
|
|
|
t_respawn = 0;
|
|
|
|
t_strafe = 0;
|
|
|
|
t_react = 0;
|
|
|
|
t_fight = 0;
|
|
|
|
t_roam = 0;
|
|
|
|
t_rocket = 0;
|
|
|
|
first_shot = true;
|
|
|
|
sleft = false;
|
|
|
|
allround = false;
|
|
|
|
increase = false;
|
2016-03-25 23:34:56 +00:00
|
|
|
old = { 0, 0 };
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
2016-09-19 22:41:22 +00:00
|
|
|
FSerializer &Serialize(FSerializer &arc, const char *key, botskill_t &skill, botskill_t *def)
|
|
|
|
{
|
|
|
|
if (arc.BeginObject(key))
|
|
|
|
{
|
|
|
|
arc("aiming", skill.aiming)
|
|
|
|
("perfection", skill.perfection)
|
|
|
|
("reaction", skill.reaction)
|
|
|
|
("isp", skill.isp)
|
|
|
|
.EndObject();
|
|
|
|
}
|
|
|
|
return arc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DBot::Serialize(FSerializer &arc)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
Super::Serialize (arc);
|
|
|
|
|
2016-09-19 22:41:22 +00:00
|
|
|
arc("player", player)
|
|
|
|
("angle", Angle)
|
|
|
|
("dest", dest)
|
|
|
|
("prev", prev)
|
|
|
|
("enemy", enemy)
|
|
|
|
("missile", missile)
|
|
|
|
("mate", mate)
|
|
|
|
("lastmate", last_mate)
|
|
|
|
("skill", skill)
|
|
|
|
("active", t_active)
|
|
|
|
("respawn", t_respawn)
|
|
|
|
("strafe", t_strafe)
|
|
|
|
("react", t_react)
|
|
|
|
("fight", t_fight)
|
|
|
|
("roam", t_roam)
|
|
|
|
("rocket", t_rocket)
|
|
|
|
("firstshot", first_shot)
|
|
|
|
("sleft", sleft)
|
|
|
|
("allround", allround)
|
|
|
|
("increase", increase)
|
|
|
|
("old", old);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DBot::Tick ()
|
|
|
|
{
|
|
|
|
Super::Tick ();
|
|
|
|
|
2019-01-28 19:15:48 +00:00
|
|
|
if (player->mo == nullptr || Level->isFrozen())
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BotThinkCycles.Clock();
|
2019-01-30 00:38:18 +00:00
|
|
|
Level->BotInfo.m_Thinking = true;
|
2016-03-01 15:47:10 +00:00
|
|
|
Think ();
|
2019-01-30 00:38:18 +00:00
|
|
|
Level->BotInfo.m_Thinking = false;
|
2016-03-01 15:47:10 +00:00
|
|
|
BotThinkCycles.Unclock();
|
|
|
|
}
|
|
|
|
|
|
|
|
CVAR (Int, bot_next_color, 11, 0)
|
|
|
|
|
|
|
|
CCMD (addbot)
|
|
|
|
{
|
|
|
|
if (gamestate != GS_LEVEL && gamestate != GS_INTERMISSION)
|
|
|
|
{
|
|
|
|
Printf ("Bots cannot be added when not in a game!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!players[consoleplayer].settings_controller)
|
|
|
|
{
|
|
|
|
Printf ("Only setting controllers can add bots\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argv.argc() > 2)
|
|
|
|
{
|
|
|
|
Printf ("addbot [botname] : add a bot to the game\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argv.argc() > 1)
|
2019-02-01 23:24:43 +00:00
|
|
|
primaryLevel->BotInfo.SpawnBot (argv[1]);
|
2016-03-01 15:47:10 +00:00
|
|
|
else
|
2019-02-01 23:24:43 +00:00
|
|
|
primaryLevel->BotInfo.SpawnBot (nullptr);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FCajunMaster::ClearPlayer (int i, bool keepTeam)
|
|
|
|
{
|
|
|
|
if (players[i].mo)
|
|
|
|
{
|
|
|
|
players[i].mo->Destroy ();
|
2019-01-06 08:39:35 +00:00
|
|
|
players[i].mo = nullptr;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
botinfo_t *bot = botinfo;
|
|
|
|
while (bot && stricmp (players[i].userinfo.GetName(), bot->name))
|
|
|
|
bot = bot->next;
|
|
|
|
if (bot)
|
|
|
|
{
|
|
|
|
bot->inuse = BOTINUSE_No;
|
|
|
|
bot->lastteam = keepTeam ? players[i].userinfo.GetTeam() : TEAM_NONE;
|
|
|
|
}
|
2019-01-06 08:39:35 +00:00
|
|
|
if (players[i].Bot != nullptr)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
players[i].Bot->Destroy ();
|
2019-01-06 08:39:35 +00:00
|
|
|
players[i].Bot = nullptr;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
players[i].~player_t();
|
|
|
|
::new(&players[i]) player_t;
|
|
|
|
players[i].userinfo.Reset();
|
|
|
|
playeringame[i] = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (removebots)
|
|
|
|
{
|
|
|
|
if (!players[consoleplayer].settings_controller)
|
|
|
|
{
|
|
|
|
Printf ("Only setting controllers can remove bots\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Net_WriteByte (DEM_KILLBOTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (freeze)
|
|
|
|
{
|
|
|
|
if (CheckCheatmode ())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (netgame && !players[consoleplayer].settings_controller)
|
|
|
|
{
|
|
|
|
Printf ("Only setting controllers can use freeze mode\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
|
|
Net_WriteByte (CHT_FREEZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (listbots)
|
|
|
|
{
|
2019-02-01 23:24:43 +00:00
|
|
|
botinfo_t *thebot = primaryLevel->BotInfo.botinfo;
|
2016-03-01 15:47:10 +00:00
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
while (thebot)
|
|
|
|
{
|
|
|
|
Printf ("%s%s\n", thebot->name, thebot->inuse == BOTINUSE_Yes ? " (active)" : "");
|
|
|
|
thebot = thebot->next;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
Printf ("> %d bots\n", count);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the bot specific weapon information
|
|
|
|
// This is intentionally not in the weapon definition anymore.
|
|
|
|
|
2018-11-24 22:48:23 +00:00
|
|
|
BotInfoMap BotInfo;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2018-11-24 22:48:23 +00:00
|
|
|
void InitBotStuff()
|
|
|
|
{
|
|
|
|
int lump;
|
|
|
|
int lastlump = 0;
|
|
|
|
while (-1 != (lump = Wads.FindLump("BOTSUPP", &lastlump)))
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2018-11-24 22:48:23 +00:00
|
|
|
FScanner sc(lump);
|
|
|
|
sc.SetCMode(true);
|
|
|
|
while (sc.GetString())
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2018-11-24 22:48:23 +00:00
|
|
|
PClassActor *wcls = PClass::FindActor(sc.String);
|
2019-01-06 08:39:35 +00:00
|
|
|
if (wcls != nullptr && wcls->IsDescendantOf(NAME_Weapon))
|
2018-11-24 22:48:23 +00:00
|
|
|
{
|
|
|
|
BotInfoData bi = {};
|
|
|
|
sc.MustGetStringName(",");
|
|
|
|
sc.MustGetNumber();
|
|
|
|
bi.MoveCombatDist = sc.Number;
|
|
|
|
while (sc.CheckString(","))
|
|
|
|
{
|
|
|
|
sc.MustGetString();
|
|
|
|
if (sc.Compare("BOT_REACTION_SKILL_THING"))
|
|
|
|
{
|
|
|
|
bi.flags |= BIF_BOT_REACTION_SKILL_THING;
|
|
|
|
}
|
|
|
|
else if (sc.Compare("BOT_EXPLOSIVE"))
|
|
|
|
{
|
|
|
|
bi.flags |= BIF_BOT_EXPLOSIVE;
|
|
|
|
}
|
|
|
|
else if (sc.Compare("BOT_BFG"))
|
|
|
|
{
|
|
|
|
bi.flags |= BIF_BOT_BFG;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PClassActor *cls = PClass::FindActor(sc.String);
|
|
|
|
bi.projectileType = cls;
|
|
|
|
if (cls == nullptr)
|
|
|
|
{
|
|
|
|
sc.ScriptError("Unknown token %s", sc.String);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BotInfo[wcls->TypeName] = bi;
|
|
|
|
}
|
|
|
|
else
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2018-11-24 22:48:23 +00:00
|
|
|
sc.ScriptError("%s is not a weapon type", sc.String);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-24 22:48:23 +00:00
|
|
|
// Fixme: Export these, too.
|
2016-03-01 15:47:10 +00:00
|
|
|
static const char *warnbotmissiles[] = { "PlasmaBall", "Ripper", "HornRodFX1" };
|
|
|
|
for(unsigned i=0;i<countof(warnbotmissiles);i++)
|
|
|
|
{
|
|
|
|
AActor *a = GetDefaultByName (warnbotmissiles[i]);
|
2019-01-06 08:39:35 +00:00
|
|
|
if (a != nullptr)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
a->flags3|=MF3_WARNBOT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|