qzdoom/src/b_bot.cpp

315 lines
7 KiB
C++
Raw Normal View History

/*
**
**
**---------------------------------------------------------------------------
** 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"
#include "doomstat.h"
#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"
#include "w_wad.h"
#include "vm.h"
#include "g_levellocals.h"
2016-03-01 15:47:10 +00:00
IMPLEMENT_CLASS(DBot, false, true)
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)
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 ();
if (player->mo == nullptr || Level->isFrozen())
2016-03-01 15:47:10 +00:00
{
return;
}
BotThinkCycles.Clock();
bglobal.m_Thinking = true;
Think ();
bglobal.m_Thinking = false;
BotThinkCycles.Unclock();
}
CVAR (Int, bot_next_color, 11, 0)
CVAR (Bool, bot_observer, false, 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)
bglobal.SpawnBot (argv[1]);
else
2019-01-06 08:39:35 +00:00
bglobal.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)
{
botinfo_t *thebot = bglobal.botinfo;
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.
BotInfoMap BotInfo;
2016-03-01 15:47:10 +00:00
void InitBotStuff()
{
int lump;
int lastlump = 0;
while (-1 != (lump = Wads.FindLump("BOTSUPP", &lastlump)))
2016-03-01 15:47:10 +00:00
{
FScanner sc(lump);
sc.SetCMode(true);
while (sc.GetString())
2016-03-01 15:47:10 +00:00
{
PClassActor *wcls = PClass::FindActor(sc.String);
2019-01-06 08:39:35 +00:00
if (wcls != nullptr && wcls->IsDescendantOf(NAME_Weapon))
{
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
{
sc.ScriptError("%s is not a weapon type", sc.String);
2016-03-01 15:47:10 +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;
}
}
}