mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge branch 'pet-robo' into 'master'
This is my PET ROBO! See merge request KartKrew/Kart!257
This commit is contained in:
commit
dcd2f790ba
36 changed files with 3540 additions and 819 deletions
|
|
@ -165,6 +165,9 @@ set(SRB2_CORE_GAME_SOURCES
|
|||
k_pwrlv.c
|
||||
k_waypoint.c
|
||||
k_color.c
|
||||
k_bot.c
|
||||
k_botitem.c
|
||||
k_botsearch.c
|
||||
|
||||
p_local.h
|
||||
p_maputl.h
|
||||
|
|
@ -184,6 +187,7 @@ set(SRB2_CORE_GAME_SOURCES
|
|||
k_pwrlv.h
|
||||
k_waypoint.h
|
||||
k_color.h
|
||||
k_bot.h
|
||||
)
|
||||
|
||||
if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
|
|
|
|||
|
|
@ -562,7 +562,9 @@ OBJS:=$(i_main_o) \
|
|||
$(OBJDIR)/i_tcp.o \
|
||||
$(OBJDIR)/lzf.o \
|
||||
$(OBJDIR)/vid_copy.o \
|
||||
$(OBJDIR)/b_bot.o \
|
||||
$(OBJDIR)/k_bot.o \
|
||||
$(OBJDIR)/k_botitem.o \
|
||||
$(OBJDIR)/k_botsearch.o \
|
||||
$(i_cdmus_o) \
|
||||
$(i_net_o) \
|
||||
$(i_system_o) \
|
||||
|
|
|
|||
277
src/b_bot.c
277
src/b_bot.c
|
|
@ -1,277 +0,0 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2007-2016 by John "JTE" Muniz.
|
||||
// Copyright (C) 2011-2018 by Sonic Team Junior.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file b_bot.c
|
||||
/// \brief Basic bot handling
|
||||
|
||||
#include "doomdef.h"
|
||||
#include "d_player.h"
|
||||
#include "g_game.h"
|
||||
#include "r_main.h"
|
||||
#include "p_local.h"
|
||||
#include "b_bot.h"
|
||||
#include "lua_hook.h"
|
||||
|
||||
// If you want multiple bots, variables like this will
|
||||
// have to be stuffed in something accessible through player_t.
|
||||
static boolean lastForward = false;
|
||||
static boolean lastBlocked = false;
|
||||
static boolean blocked = false;
|
||||
|
||||
static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
||||
{
|
||||
boolean forward=false, backward=false, left=false, right=false, jump=false, spin=false;
|
||||
angle_t angle;
|
||||
INT16 rangle;
|
||||
fixed_t dist;
|
||||
|
||||
// We can't follow Sonic if he's not around!
|
||||
if (!sonic || sonic->health <= 0)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_BLUA
|
||||
// Lua can handle it!
|
||||
if (LUAh_BotAI(sonic, tails, cmd))
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (tails->player->pflags & (PF_MACESPIN|PF_ITEMHANG))
|
||||
{
|
||||
dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y);
|
||||
if (sonic->player->cmd.buttons & BT_DRIFT && sonic->player->pflags & (PF_JUMPED|PF_MACESPIN|PF_ITEMHANG))
|
||||
cmd->buttons |= BT_DRIFT;
|
||||
if (sonic->player->pflags & (PF_MACESPIN|PF_ITEMHANG))
|
||||
{
|
||||
cmd->forwardmove = sonic->player->cmd.forwardmove;
|
||||
cmd->angleturn = abs((signed)(tails->angle - sonic->angle))>>16;
|
||||
if (sonic->angle < tails->angle)
|
||||
cmd->angleturn = -cmd->angleturn;
|
||||
} else if (dist > FixedMul(512*FRACUNIT, tails->scale))
|
||||
cmd->buttons |= BT_DRIFT;
|
||||
return;
|
||||
}
|
||||
|
||||
// Gather data about the environment
|
||||
dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y);
|
||||
if (tails->player->pflags & PF_STARTDASH)
|
||||
angle = sonic->angle;
|
||||
else
|
||||
angle = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y);
|
||||
|
||||
// Decide which direction to turn
|
||||
angle = (tails->angle - angle);
|
||||
if (angle < ANGLE_180) {
|
||||
right = true; // We need to turn right
|
||||
rangle = AngleFixed(angle)>>FRACBITS;
|
||||
} else {
|
||||
left = true; // We need to turn left
|
||||
rangle = 360-(AngleFixed(angle)>>FRACBITS);
|
||||
}
|
||||
|
||||
// Decide to move forward if you're finished turning
|
||||
if (abs(rangle) < 10) { // We're facing the right way?
|
||||
left = right = false; // Stop turning
|
||||
forward = true; // and walk forward instead.
|
||||
}
|
||||
if (dist < (sonic->radius+tails->radius)*3) // We're close enough?
|
||||
forward = false; // Stop walking.
|
||||
|
||||
// Decide when to jump
|
||||
if (!(tails->player->pflags & (PF_JUMPED|PF_JUMPDOWN))) { // We're not jumping yet...
|
||||
if (forward && lastForward && blocked && lastBlocked) // We've been stopped by a wall or something
|
||||
jump = true; // Try to jump up
|
||||
} else if ((tails->player->pflags & (PF_JUMPDOWN|PF_JUMPED)) == (PF_JUMPDOWN|PF_JUMPED)) { // When we're already jumping...
|
||||
if (lastForward && blocked) // We're still stuck on something?
|
||||
jump = true;
|
||||
if (sonic->floorz > tails->floorz) // He's still above us? Jump HIGHER, then!
|
||||
jump = true;
|
||||
}
|
||||
|
||||
// Decide when to spin
|
||||
if (sonic->player->pflags & PF_STARTDASH
|
||||
&& (tails->player->pflags & PF_STARTDASH || (P_AproxDistance(tails->momx, tails->momy) < 2*FRACUNIT && !forward)))
|
||||
spin = true;
|
||||
|
||||
// Turn the virtual keypresses into ticcmd_t.
|
||||
B_KeysToTiccmd(tails, cmd, forward, backward, left, right, false, false, jump, spin);
|
||||
|
||||
// Update our status
|
||||
lastForward = forward;
|
||||
lastBlocked = blocked;
|
||||
blocked = false;
|
||||
}
|
||||
|
||||
void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
|
||||
{
|
||||
// Can't build a ticcmd if we aren't spawned...
|
||||
if (!player->mo)
|
||||
return;
|
||||
|
||||
if (player->playerstate == PST_DEAD)
|
||||
{
|
||||
if (B_CheckRespawn(player))
|
||||
cmd->buttons |= BT_DRIFT;
|
||||
return;
|
||||
}
|
||||
|
||||
// Bot AI isn't programmed in analog.
|
||||
//CV_SetValue(&cv_analog2, false);
|
||||
|
||||
#ifdef HAVE_BLUA
|
||||
// Let Lua scripts build ticcmds
|
||||
if (LUAh_BotTiccmd(player, cmd))
|
||||
return;
|
||||
#endif
|
||||
|
||||
// We don't have any main character AI, sorry. D:
|
||||
if (player-players == consoleplayer)
|
||||
return;
|
||||
|
||||
// Basic Tails AI
|
||||
B_BuildTailsTiccmd(players[consoleplayer].mo, player->mo, cmd);
|
||||
}
|
||||
|
||||
void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin)
|
||||
{
|
||||
// Turn the virtual keypresses into ticcmd_t.
|
||||
if (twodlevel || mo->flags2 & MF2_TWOD) {
|
||||
if (players[consoleplayer].climbing
|
||||
|| mo->player->pflags & PF_GLIDING) {
|
||||
// Don't mess with bot inputs during these unhandled movement conditions.
|
||||
// The normal AI doesn't use abilities, so custom AI should be sending us exactly what it wants anyway.
|
||||
if (forward)
|
||||
cmd->forwardmove += MAXPLMOVE<<FRACBITS>>16;
|
||||
if (backward)
|
||||
cmd->forwardmove -= MAXPLMOVE<<FRACBITS>>16;
|
||||
if (left || strafeleft)
|
||||
cmd->sidemove -= MAXPLMOVE<<FRACBITS>>16;
|
||||
if (right || straferight)
|
||||
cmd->sidemove += MAXPLMOVE<<FRACBITS>>16;
|
||||
} else {
|
||||
// In standard 2D mode, interpret "forward" as "the way you're facing" and everything else as "the way you're not facing"
|
||||
if (left || right)
|
||||
backward = true;
|
||||
left = right = false;
|
||||
if (forward) {
|
||||
if (mo->angle < ANGLE_90 || mo->angle > ANGLE_270)
|
||||
right = true;
|
||||
else
|
||||
left = true;
|
||||
} else if (backward) {
|
||||
if (mo->angle < ANGLE_90 || mo->angle > ANGLE_270)
|
||||
left = true;
|
||||
else
|
||||
right = true;
|
||||
}
|
||||
if (left || strafeleft)
|
||||
cmd->sidemove -= MAXPLMOVE<<FRACBITS>>16;
|
||||
if (right || straferight)
|
||||
cmd->sidemove += MAXPLMOVE<<FRACBITS>>16;
|
||||
}
|
||||
} else {
|
||||
if (forward)
|
||||
cmd->forwardmove += MAXPLMOVE<<FRACBITS>>16;
|
||||
if (backward)
|
||||
cmd->forwardmove -= MAXPLMOVE<<FRACBITS>>16;
|
||||
if (left)
|
||||
cmd->angleturn += 1280;
|
||||
if (right)
|
||||
cmd->angleturn -= 1280;
|
||||
if (strafeleft)
|
||||
cmd->sidemove -= MAXPLMOVE<<FRACBITS>>16;
|
||||
if (straferight)
|
||||
cmd->sidemove += MAXPLMOVE<<FRACBITS>>16;
|
||||
}
|
||||
if (jump)
|
||||
cmd->buttons |= BT_DRIFT;
|
||||
if (spin)
|
||||
cmd->buttons |= BT_BRAKE;
|
||||
}
|
||||
|
||||
void B_MoveBlocked(player_t *player)
|
||||
{
|
||||
(void)player;
|
||||
blocked = true;
|
||||
}
|
||||
|
||||
boolean B_CheckRespawn(player_t *player)
|
||||
{
|
||||
mobj_t *sonic = players[consoleplayer].mo;
|
||||
mobj_t *tails = player->mo;
|
||||
|
||||
// We can't follow Sonic if he's not around!
|
||||
if (!sonic || sonic->health <= 0)
|
||||
return false;
|
||||
|
||||
// Check if Sonic is busy first.
|
||||
// If he's doing any of these things, he probably doesn't want to see us.
|
||||
if (sonic->player->pflags & (PF_ROPEHANG|PF_GLIDING|PF_CARRIED|PF_SLIDING|PF_ITEMHANG|PF_MACESPIN|PF_NIGHTSMODE)
|
||||
|| (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK))
|
||||
return false;
|
||||
|
||||
// Low ceiling, do not want!
|
||||
if (sonic->ceilingz - sonic->z < 2*sonic->height)
|
||||
return false;
|
||||
|
||||
// If you're dead, wait a few seconds to respawn.
|
||||
if (player->playerstate == PST_DEAD) {
|
||||
if (player->deadtimer > 4*TICRATE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If you can't see Sonic, I guess we should?
|
||||
if (!P_CheckSight(sonic, tails) && P_AproxDistance(P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y), tails->z-sonic->z) > FixedMul(1024*FRACUNIT, tails->scale))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void B_RespawnBot(INT32 playernum)
|
||||
{
|
||||
player_t *player = &players[playernum];
|
||||
fixed_t x,y,z;
|
||||
mobj_t *sonic = players[consoleplayer].mo;
|
||||
mobj_t *tails;
|
||||
|
||||
if (!sonic || sonic->health <= 0)
|
||||
return;
|
||||
|
||||
player->bot = 1;
|
||||
P_SpawnPlayer(playernum);
|
||||
tails = player->mo;
|
||||
|
||||
x = sonic->x;
|
||||
y = sonic->y;
|
||||
if (sonic->eflags & MFE_VERTICALFLIP) {
|
||||
tails->eflags |= MFE_VERTICALFLIP;
|
||||
z = sonic->z - FixedMul(512*FRACUNIT,sonic->scale);
|
||||
if (z < sonic->floorz)
|
||||
z = sonic->floorz;
|
||||
} else {
|
||||
z = sonic->z + sonic->height + FixedMul(512*FRACUNIT,sonic->scale);
|
||||
if (z > sonic->ceilingz - sonic->height)
|
||||
z = sonic->ceilingz - sonic->height;
|
||||
}
|
||||
|
||||
if (sonic->flags2 & MF2_OBJECTFLIP)
|
||||
tails->flags2 |= MF2_OBJECTFLIP;
|
||||
if (sonic->flags2 & MF2_TWOD)
|
||||
tails->flags2 |= MF2_TWOD;
|
||||
if (sonic->eflags & MFE_UNDERWATER)
|
||||
tails->eflags |= MFE_UNDERWATER;
|
||||
player->powers[pw_underwater] = sonic->player->powers[pw_underwater];
|
||||
player->powers[pw_spacetime] = sonic->player->powers[pw_spacetime];
|
||||
player->powers[pw_gravityboots] = sonic->player->powers[pw_gravityboots];
|
||||
player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol];
|
||||
|
||||
P_TeleportMove(tails, x, y, z);
|
||||
P_SetPlayerMobjState(tails, S_KART_STILL1); // SRB2kart - was S_PLAY_FALL1
|
||||
P_SetScale(tails, sonic->scale);
|
||||
tails->destscale = sonic->destscale;
|
||||
}
|
||||
17
src/b_bot.h
17
src/b_bot.h
|
|
@ -1,17 +0,0 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2007-2016 by John "JTE" Muniz.
|
||||
// Copyright (C) 2012-2018 by Sonic Team Junior.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file b_bot.h
|
||||
/// \brief Basic bot handling
|
||||
|
||||
void B_BuildTiccmd(player_t *player, ticcmd_t *cmd);
|
||||
void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin);
|
||||
boolean B_CheckRespawn(player_t *player);
|
||||
void B_MoveBlocked(player_t *player);
|
||||
void B_RespawnBot(INT32 playernum);
|
||||
113
src/d_clisrv.c
113
src/d_clisrv.c
|
|
@ -48,6 +48,7 @@
|
|||
#include "k_kart.h"
|
||||
#include "k_battle.h"
|
||||
#include "k_pwrlv.h"
|
||||
#include "k_bot.h"
|
||||
|
||||
#ifdef CLIENT_LOADINGSCREEN
|
||||
// cl loading screen
|
||||
|
|
@ -645,6 +646,12 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
|
|||
|
||||
rsp->splitscreenindex = players[i].splitscreenindex;
|
||||
|
||||
rsp->bot = players[i].bot;
|
||||
rsp->bot_difficulty = players[i].botvars.difficulty;
|
||||
rsp->bot_itemdelay = players[i].botvars.itemdelay;
|
||||
rsp->bot_itemconfirm = players[i].botvars.itemconfirm;
|
||||
rsp->bot_turnconfirm = players[i].botvars.turnconfirm;
|
||||
|
||||
rsp->hasmo = false;
|
||||
//Transfer important mo information if the player has a body.
|
||||
//This lets us resync players even if they are dead.
|
||||
|
|
@ -768,6 +775,12 @@ static void resynch_read_player(resynch_pak *rsp)
|
|||
|
||||
players[i].splitscreenindex = rsp->splitscreenindex;
|
||||
|
||||
players[i].bot = rsp->bot;
|
||||
players[i].botvars.difficulty = rsp->bot_difficulty;
|
||||
players[i].botvars.itemdelay = rsp->bot_itemdelay;
|
||||
players[i].botvars.itemconfirm = rsp->bot_itemconfirm;
|
||||
players[i].botvars.turnconfirm = rsp->bot_turnconfirm;
|
||||
|
||||
//We get a packet for each player in game.
|
||||
if (!playeringame[i])
|
||||
return;
|
||||
|
|
@ -1298,8 +1311,6 @@ static boolean CL_SendJoin(void)
|
|||
|
||||
if (splitscreen)
|
||||
localplayers += splitscreen;
|
||||
else if (botingame)
|
||||
localplayers++;
|
||||
|
||||
netbuffer->u.clientcfg.localplayers = localplayers;
|
||||
netbuffer->u.clientcfg._255 = 255;
|
||||
|
|
@ -1550,6 +1561,8 @@ static boolean SV_SendServerConfig(INT32 node)
|
|||
|
||||
netbuffer->u.servercfg.playerskins[i] = (UINT8)players[i].skin;
|
||||
netbuffer->u.servercfg.playercolor[i] = (UINT8)players[i].skincolor;
|
||||
|
||||
netbuffer->u.servercfg.playerisbot[i] = players[i].bot;
|
||||
}
|
||||
|
||||
memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
|
||||
|
|
@ -2607,8 +2620,7 @@ static void Command_connect(void)
|
|||
splitscreen = cv_splitplayers.value-1;
|
||||
SplitScreen_OnChange();
|
||||
}
|
||||
botingame = false;
|
||||
botskin = 0;
|
||||
|
||||
CL_ConnectToServer(viams);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -2659,7 +2671,7 @@ void CL_RemovePlayer(INT32 playernum, INT32 reason)
|
|||
|
||||
demo_extradata[playernum] |= DXD_PLAYSTATE;
|
||||
|
||||
if (server && !demo.playback)
|
||||
if (server && !demo.playback && !players[playernum].bot)
|
||||
{
|
||||
INT32 node = playernode[playernum];
|
||||
//playerpernode[node] = 0; // It'd be better to remove them all at once, but ghosting happened, so continue to let CL_RemovePlayer do it one-by-one
|
||||
|
|
@ -3283,6 +3295,7 @@ consvar_t cv_downloadspeed = {"downloadspeed", "16", CV_SAVE, downloadspeed_cons
|
|||
|
||||
static void Got_AddPlayer(UINT8 **p, INT32 playernum);
|
||||
static void Got_RemovePlayer(UINT8 **p, INT32 playernum);
|
||||
static void Got_AddBot(UINT8 **p, INT32 playernum);
|
||||
|
||||
// called one time at init
|
||||
void D_ClientServerInit(void)
|
||||
|
|
@ -3312,6 +3325,7 @@ void D_ClientServerInit(void)
|
|||
RegisterNetXCmd(XD_KICK, Got_KickCmd);
|
||||
RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer);
|
||||
RegisterNetXCmd(XD_REMOVEPLAYER, Got_RemovePlayer);
|
||||
RegisterNetXCmd(XD_ADDBOT, Got_AddBot);
|
||||
#ifndef NONET
|
||||
#ifdef DUMPCONSISTENCY
|
||||
CV_RegisterVar(&cv_dumpconsistency);
|
||||
|
|
@ -3524,8 +3538,6 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
|||
displayplayers[splitscreenplayer] = newplayernum;
|
||||
g_localplayers[splitscreenplayer] = newplayernum;
|
||||
DEBFILE(va("spawning sister # %d\n", splitscreenplayer));
|
||||
if (splitscreenplayer == 1 && botingame)
|
||||
players[newplayernum].bot = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -3544,6 +3556,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
|||
}
|
||||
|
||||
players[newplayernum].splitscreenindex = splitscreenplayer;
|
||||
players[newplayernum].bot = false;
|
||||
|
||||
playerconsole[newplayernum] = console;
|
||||
splitscreen_original_party_size[console] =
|
||||
|
|
@ -3597,6 +3610,63 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
|
|||
CL_RemovePlayer(pnum, reason);
|
||||
}
|
||||
|
||||
// Xcmd XD_ADDBOT
|
||||
// Compacted version of XD_ADDPLAYER for simplicity
|
||||
static void Got_AddBot(UINT8 **p, INT32 playernum)
|
||||
{
|
||||
INT16 newplayernum;
|
||||
UINT8 skinnum = 0;
|
||||
UINT8 difficulty = MAXBOTDIFFICULTY;
|
||||
|
||||
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
|
||||
{
|
||||
// protect against hacked/buggy client
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]);
|
||||
if (server)
|
||||
{
|
||||
XBOXSTATIC UINT8 buf[2];
|
||||
|
||||
buf[0] = (UINT8)playernum;
|
||||
buf[1] = KICK_MSG_CON_FAIL;
|
||||
SendNetXCmd(XD_KICK, &buf, 2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
newplayernum = (UINT8)READUINT8(*p);
|
||||
skinnum = (UINT8)READUINT8(*p);
|
||||
difficulty = (UINT8)READUINT8(*p);
|
||||
|
||||
CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum);
|
||||
|
||||
// Clear player before joining, lest some things get set incorrectly
|
||||
CL_ClearPlayer(newplayernum);
|
||||
|
||||
playeringame[newplayernum] = true;
|
||||
G_AddPlayer(newplayernum);
|
||||
if (newplayernum+1 > doomcom->numslots)
|
||||
doomcom->numslots = (INT16)(newplayernum+1);
|
||||
|
||||
playernode[newplayernum] = servernode;
|
||||
|
||||
players[newplayernum].splitscreenindex = 0;
|
||||
players[newplayernum].bot = true;
|
||||
players[newplayernum].botvars.difficulty = difficulty;
|
||||
|
||||
players[newplayernum].skincolor = skins[skinnum].prefcolor;
|
||||
sprintf(player_names[newplayernum], "%s", skins[skinnum].realname);
|
||||
SetPlayerSkinByNum(newplayernum, skinnum);
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false);
|
||||
}
|
||||
|
||||
#ifdef HAVE_BLUA
|
||||
LUAh_PlayerJoin(newplayernum);
|
||||
#endif
|
||||
}
|
||||
|
||||
static boolean SV_AddWaitingPlayers(void)
|
||||
{
|
||||
INT32 node, n, newplayer = false;
|
||||
|
|
@ -3614,6 +3684,7 @@ static boolean SV_AddWaitingPlayers(void)
|
|||
{
|
||||
UINT8 buf[4];
|
||||
UINT8 *buf_p = buf;
|
||||
UINT8 nobotoverwrite;
|
||||
|
||||
newplayer = true;
|
||||
|
||||
|
|
@ -3631,6 +3702,21 @@ static boolean SV_AddWaitingPlayers(void)
|
|||
break;
|
||||
}
|
||||
|
||||
nobotoverwrite = newplayernum;
|
||||
|
||||
while (playeringame[nobotoverwrite]
|
||||
&& players[nobotoverwrite].bot
|
||||
&& nobotoverwrite < MAXPLAYERS)
|
||||
{
|
||||
// Only overwrite bots if there are NO other slots available.
|
||||
nobotoverwrite++;
|
||||
}
|
||||
|
||||
if (nobotoverwrite < MAXPLAYERS)
|
||||
{
|
||||
newplayernum = nobotoverwrite;
|
||||
}
|
||||
|
||||
// should never happen since we check the playernum
|
||||
// before accepting the join
|
||||
I_Assert(newplayernum < MAXPLAYERS);
|
||||
|
|
@ -4130,6 +4216,7 @@ static void HandlePacketFromAwayNode(SINT8 node)
|
|||
playeringame[j] = true;
|
||||
SetPlayerSkinByNum(j, (INT32)netbuffer->u.servercfg.playerskins[j]);
|
||||
players[j].skincolor = netbuffer->u.servercfg.playercolor[j];
|
||||
players[j].bot = netbuffer->u.servercfg.playerisbot[j];
|
||||
}
|
||||
|
||||
scp = netbuffer->u.servercfg.varlengthinputs;
|
||||
|
|
@ -4965,7 +5052,7 @@ static void CL_SendClientCmd(void)
|
|||
G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
|
||||
netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%TICQUEUE]);
|
||||
|
||||
if (splitscreen || botingame) // Send a special packet with 2 cmd for splitscreen
|
||||
if (splitscreen) // Send a special packet with 2 cmd for splitscreen
|
||||
{
|
||||
netbuffer->packettype = (mis ? PT_CLIENT2MIS : PT_CLIENT2CMD);
|
||||
packetsize = sizeof (client2cmd_pak);
|
||||
|
|
@ -5164,7 +5251,7 @@ static void Local_Maketic(INT32 realtics)
|
|||
if (!dedicated) rendergametic = gametic;
|
||||
// translate inputs (keyboard/mouse/joystick) into game controls
|
||||
G_BuildTiccmd(&localcmds, realtics, 1);
|
||||
if (splitscreen || botingame)
|
||||
if (splitscreen)
|
||||
{
|
||||
G_BuildTiccmd(&localcmds2, realtics, 2);
|
||||
if (splitscreen > 1)
|
||||
|
|
@ -5637,9 +5724,15 @@ FILESTAMP
|
|||
INT32 D_NumPlayers(void)
|
||||
{
|
||||
INT32 num = 0, ix;
|
||||
|
||||
for (ix = 0; ix < MAXPLAYERS; ix++)
|
||||
if (playeringame[ix])
|
||||
{
|
||||
if (playeringame[ix] && !players[ix].bot)
|
||||
{
|
||||
num++;
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -285,6 +285,12 @@ typedef struct
|
|||
|
||||
UINT8 splitscreenindex;
|
||||
|
||||
boolean bot;
|
||||
UINT8 bot_difficulty;
|
||||
tic_t bot_itemdelay;
|
||||
tic_t bot_itemconfirm;
|
||||
SINT8 bot_turnconfirm;
|
||||
|
||||
//player->mo stuff
|
||||
UINT8 hasmo; // Boolean
|
||||
|
||||
|
|
@ -328,6 +334,8 @@ typedef struct
|
|||
UINT8 playerskins[MAXPLAYERS];
|
||||
UINT8 playercolor[MAXPLAYERS];
|
||||
|
||||
UINT8 playerisbot[MAXPLAYERS];
|
||||
|
||||
UINT8 gametype;
|
||||
UINT8 modifiedgame;
|
||||
SINT8 adminplayers[MAXPLAYERS]; // Needs to be signed
|
||||
|
|
|
|||
|
|
@ -772,8 +772,7 @@ void D_StartTitle(void)
|
|||
|
||||
splitscreen = 0;
|
||||
SplitScreen_OnChange();
|
||||
botingame = false;
|
||||
botskin = 0;
|
||||
|
||||
cv_debug = 0;
|
||||
emeralds = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -392,6 +392,21 @@ consvar_t cv_kartspeedometer = {"kartdisplayspeed", "Off", CV_SAVE, kartspeedome
|
|||
static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}};
|
||||
consvar_t cv_kartvoices = {"kartvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
static CV_PossibleValue_t kartbot_cons_t[] = {
|
||||
{0, "Off"},
|
||||
{1, "Lv.1"},
|
||||
{2, "Lv.2"},
|
||||
{3, "Lv.3"},
|
||||
{4, "Lv.4"},
|
||||
{5, "Lv.5"},
|
||||
{6, "Lv.6"},
|
||||
{7, "Lv.7"},
|
||||
{8, "Lv.8"},
|
||||
{9, "Lv.9"},
|
||||
{0, NULL}
|
||||
};
|
||||
consvar_t cv_kartbot = {"kartbot", "0", CV_NETVAR|CV_CHEAT, kartbot_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
consvar_t cv_karteliminatelast = {"karteliminatelast", "Yes", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOSHOWHELP, CV_YesNo, KartEliminateLast_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
consvar_t cv_kartusepwrlv = {"kartusepwrlv", "Yes", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
|
@ -549,6 +564,7 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
|
|||
"LEAVEPARTY",
|
||||
"CANCELPARTYINVITE",
|
||||
"GIVEITEM",
|
||||
"ADDBOT",
|
||||
#ifdef HAVE_BLUA
|
||||
"LUACMD",
|
||||
"LUAVAR"
|
||||
|
|
@ -1553,7 +1569,7 @@ static void SendNameAndColor2(void)
|
|||
XBOXSTATIC char buf[MAXPLAYERNAME+2];
|
||||
char *p;
|
||||
|
||||
if (splitscreen < 1 && !botingame)
|
||||
if (splitscreen < 1)
|
||||
return; // can happen if skin2/color2/name2 changed
|
||||
|
||||
if (g_localplayers[1] != consoleplayer)
|
||||
|
|
@ -1591,15 +1607,7 @@ static void SendNameAndColor2(void)
|
|||
return;
|
||||
|
||||
// If you're not in a netgame, merely update the skin, color, and name.
|
||||
if (botingame)
|
||||
{
|
||||
players[secondplaya].skincolor = botcolor;
|
||||
if (players[secondplaya].mo)
|
||||
players[secondplaya].mo->color = players[secondplaya].skincolor;
|
||||
SetPlayerSkinByNum(secondplaya, botskin-1);
|
||||
return;
|
||||
}
|
||||
else if (!netgame)
|
||||
if (!netgame)
|
||||
{
|
||||
INT32 foundskin;
|
||||
|
||||
|
|
@ -1829,15 +1837,7 @@ static void SendNameAndColor4(void)
|
|||
return;
|
||||
|
||||
// If you're not in a netgame, merely update the skin, color, and name.
|
||||
if (botingame)
|
||||
{
|
||||
players[fourthplaya].skincolor = botcolor;
|
||||
if (players[fourthplaya].mo)
|
||||
players[fourthplaya].mo->color = players[fourthplaya].skincolor;
|
||||
SetPlayerSkinByNum(fourthplaya, botskin-1);
|
||||
return;
|
||||
}
|
||||
else if (!netgame)
|
||||
if (!netgame)
|
||||
{
|
||||
INT32 foundskin;
|
||||
|
||||
|
|
@ -2247,7 +2247,7 @@ static void Got_LeaveParty(UINT8 **cp,INT32 playernum)
|
|||
void D_SendPlayerConfig(void)
|
||||
{
|
||||
SendNameAndColor();
|
||||
if (splitscreen || botingame)
|
||||
if (splitscreen)
|
||||
SendNameAndColor2();
|
||||
if (splitscreen > 1)
|
||||
SendNameAndColor3();
|
||||
|
|
@ -2808,29 +2808,6 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
|
|||
return;
|
||||
}
|
||||
|
||||
// Kick bot from special stages
|
||||
if (botskin)
|
||||
{
|
||||
if (G_IsSpecialStage(mapnum))
|
||||
{
|
||||
if (botingame)
|
||||
{
|
||||
//CL_RemoveSplitscreenPlayer();
|
||||
botingame = false;
|
||||
playeringame[1] = false;
|
||||
}
|
||||
}
|
||||
else if (!botingame)
|
||||
{
|
||||
//CL_AddSplitscreenPlayer();
|
||||
botingame = true;
|
||||
displayplayers[1] = 1;
|
||||
playeringame[1] = true;
|
||||
players[1].bot = 1;
|
||||
SendNameAndColor2();
|
||||
}
|
||||
}
|
||||
|
||||
chmappending++;
|
||||
if (netgame)
|
||||
WRITEUINT32(buf_p, M_RandomizedSeed()); // random seed
|
||||
|
|
@ -2870,11 +2847,10 @@ void D_SetupVote(void)
|
|||
SendNetXCmd(XD_SETUPVOTE, buf, p - buf);
|
||||
}
|
||||
|
||||
void D_ModifyClientVote(SINT8 voted, UINT8 splitplayer)
|
||||
void D_ModifyClientVote(UINT8 player, SINT8 voted, UINT8 splitplayer)
|
||||
{
|
||||
char buf[2];
|
||||
char *p = buf;
|
||||
UINT8 player = consoleplayer;
|
||||
|
||||
if (splitplayer > 0)
|
||||
player = g_localplayers[splitplayer];
|
||||
|
|
@ -5723,8 +5699,7 @@ void Command_ExitGame_f(void)
|
|||
|
||||
splitscreen = 0;
|
||||
SplitScreen_OnChange();
|
||||
botingame = false;
|
||||
botskin = 0;
|
||||
|
||||
cv_debug = 0;
|
||||
emeralds = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ extern consvar_t cv_kartencore;
|
|||
extern consvar_t cv_kartvoterulechanges;
|
||||
extern consvar_t cv_kartspeedometer;
|
||||
extern consvar_t cv_kartvoices;
|
||||
extern consvar_t cv_kartbot;
|
||||
extern consvar_t cv_karteliminatelast;
|
||||
extern consvar_t cv_kartusepwrlv;
|
||||
|
||||
|
|
@ -187,10 +188,11 @@ typedef enum
|
|||
XD_ACCEPTPARTYINVITE, // 28
|
||||
XD_LEAVEPARTY, // 29
|
||||
XD_CANCELPARTYINVITE, // 30
|
||||
XD_GIVEITEM, // 31
|
||||
XD_GIVEITEM, // 31
|
||||
XD_ADDBOT, // 32
|
||||
#ifdef HAVE_BLUA
|
||||
XD_LUACMD, // 32
|
||||
XD_LUAVAR, // 33
|
||||
XD_LUACMD, // 33
|
||||
XD_LUAVAR, // 34
|
||||
#endif
|
||||
MAXNETXCMD
|
||||
} netxcmd_t;
|
||||
|
|
@ -246,7 +248,7 @@ void Command_Retry_f(void);
|
|||
void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
|
||||
void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect);
|
||||
void D_SetupVote(void);
|
||||
void D_ModifyClientVote(SINT8 voted, UINT8 splitplayer);
|
||||
void D_ModifyClientVote(UINT8 player, SINT8 voted, UINT8 splitplayer);
|
||||
void D_PickVote(void);
|
||||
void ObjectPlace_OnChange(void);
|
||||
boolean IsPlayerAdmin(INT32 playernum);
|
||||
|
|
|
|||
|
|
@ -420,6 +420,17 @@ typedef enum
|
|||
RW_RAIL = 32
|
||||
} ringweapons_t;
|
||||
|
||||
// player_t struct for all bot variables
|
||||
typedef struct botvars_s
|
||||
{
|
||||
UINT8 difficulty;
|
||||
|
||||
tic_t itemdelay;
|
||||
tic_t itemconfirm;
|
||||
|
||||
SINT8 turnconfirm;
|
||||
} botvars_t;
|
||||
|
||||
// ========================================================================
|
||||
// PLAYER STRUCTURE
|
||||
// ========================================================================
|
||||
|
|
@ -594,7 +605,9 @@ typedef struct player_s
|
|||
angle_t awayviewaiming; // Used for cut-away view
|
||||
|
||||
boolean spectator;
|
||||
UINT8 bot;
|
||||
|
||||
boolean bot;
|
||||
botvars_t botvars;
|
||||
|
||||
tic_t jointime; // Timer when player joins game to change skin/color
|
||||
|
||||
|
|
|
|||
|
|
@ -7695,6 +7695,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
|
|||
"MT_WAYPOINT_RISER",
|
||||
"MT_WAYPOINT_ANCHOR",
|
||||
|
||||
"MT_BOTHINT",
|
||||
|
||||
"MT_RANDOMAUDIENCE",
|
||||
|
||||
"MT_FLAYM",
|
||||
|
|
|
|||
141
src/g_game.c
141
src/g_game.c
|
|
@ -46,7 +46,7 @@
|
|||
#include "lua_script.h" // LUA_ArchiveDemo and LUA_UnArchiveDemo
|
||||
#include "lua_hook.h"
|
||||
#include "lua_libs.h" // gL (Lua state)
|
||||
#include "b_bot.h"
|
||||
#include "k_bot.h"
|
||||
#include "m_cond.h" // condition sets
|
||||
#include "md5.h" // demo checksums
|
||||
#include "k_kart.h" // SRB2kart
|
||||
|
|
@ -58,10 +58,6 @@ gameaction_t gameaction;
|
|||
gamestate_t gamestate = GS_NULL;
|
||||
UINT8 ultimatemode = false;
|
||||
|
||||
boolean botingame;
|
||||
UINT8 botskin;
|
||||
UINT8 botcolor;
|
||||
|
||||
JoyType_t Joystick;
|
||||
JoyType_t Joystick2;
|
||||
JoyType_t Joystick3;
|
||||
|
|
@ -1279,10 +1275,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
|
|||
else
|
||||
player = &players[g_localplayers[ssplayer-1]];
|
||||
|
||||
if (ssplayer == 2)
|
||||
thiscam = (player->bot == 2 ? &camera[0] : &camera[ssplayer-1]);
|
||||
else
|
||||
thiscam = &camera[ssplayer-1];
|
||||
thiscam = &camera[ssplayer-1];
|
||||
lang = localangle[ssplayer-1];
|
||||
laim = localaiming[ssplayer-1];
|
||||
th = turnheld[ssplayer-1];
|
||||
|
|
@ -1316,6 +1309,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
|
|||
return;
|
||||
}
|
||||
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ssplayer)
|
||||
{
|
||||
case 2:
|
||||
|
|
@ -1620,9 +1618,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
|
|||
|
||||
//Reset away view if a command is given.
|
||||
if ((cmd->forwardmove || cmd->sidemove || cmd->buttons)
|
||||
&& ! r_splitscreen && displayplayers[0] != consoleplayer && ssplayer == 1)
|
||||
&& !r_splitscreen && displayplayers[0] != consoleplayer && ssplayer == 1)
|
||||
displayplayers[0] = consoleplayer;
|
||||
|
||||
}
|
||||
|
||||
// User has designated that they want
|
||||
|
|
@ -1638,8 +1635,6 @@ static void UserAnalog_OnChange(void)
|
|||
|
||||
static void UserAnalog2_OnChange(void)
|
||||
{
|
||||
if (botingame)
|
||||
return;
|
||||
/*if (cv_useranalog2.value)
|
||||
CV_SetValue(&cv_analog2, 1);
|
||||
else
|
||||
|
|
@ -1648,8 +1643,6 @@ static void UserAnalog2_OnChange(void)
|
|||
|
||||
static void UserAnalog3_OnChange(void)
|
||||
{
|
||||
if (botingame)
|
||||
return;
|
||||
/*if (cv_useranalog3.value)
|
||||
CV_SetValue(&cv_analog3, 1);
|
||||
else
|
||||
|
|
@ -1658,8 +1651,6 @@ static void UserAnalog3_OnChange(void)
|
|||
|
||||
static void UserAnalog4_OnChange(void)
|
||||
{
|
||||
if (botingame)
|
||||
return;
|
||||
/*if (cv_useranalog4.value)
|
||||
CV_SetValue(&cv_analog4, 1);
|
||||
else
|
||||
|
|
@ -1685,7 +1676,7 @@ static void Analog_OnChange(void)
|
|||
|
||||
static void Analog2_OnChange(void)
|
||||
{
|
||||
if (!(splitscreen || botingame) || !cv_cam2_dist.string)
|
||||
if (!splitscreen || !cv_cam2_dist.string)
|
||||
return;
|
||||
|
||||
// cameras are not initialized at this point
|
||||
|
|
@ -1777,7 +1768,7 @@ void G_DoLoadLevel(boolean resetplayer)
|
|||
|
||||
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
{
|
||||
if (i > 0 && !(i == 1 && botingame) && r_splitscreen < i)
|
||||
if (i > 0 && r_splitscreen < i)
|
||||
g_localplayers[i] = consoleplayer;
|
||||
}
|
||||
|
||||
|
|
@ -2402,9 +2393,17 @@ void G_Ticker(boolean run)
|
|||
|
||||
if (playeringame[i])
|
||||
{
|
||||
G_CopyTiccmd(cmd, &netcmds[buf][i], 1);
|
||||
// Use the leveltime sent in the player's ticcmd to determine control lag
|
||||
cmd->latency = modeattacking ? 0 : min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1); //@TODO add a cvar to allow setting this max
|
||||
if (K_PlayerUsesBotMovement(&players[i]))
|
||||
{
|
||||
K_BuildBotTiccmd(&players[i], cmd);
|
||||
cmd->latency = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
G_CopyTiccmd(cmd, &netcmds[buf][i], 1);
|
||||
// Use the leveltime sent in the player's ticcmd to determine control lag
|
||||
cmd->latency = modeattacking ? 0 : min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1); //@TODO add a cvar to allow setting this max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2591,7 +2590,8 @@ void G_PlayerReborn(INT32 player)
|
|||
tic_t jointime;
|
||||
UINT8 splitscreenindex;
|
||||
boolean spectator;
|
||||
INT16 bot;
|
||||
boolean bot;
|
||||
UINT8 botdifficulty;
|
||||
SINT8 pity;
|
||||
|
||||
// SRB2kart
|
||||
|
|
@ -2644,6 +2644,7 @@ void G_PlayerReborn(INT32 player)
|
|||
|
||||
mare = players[player].mare;
|
||||
bot = players[player].bot;
|
||||
botdifficulty = players[player].botvars.difficulty;
|
||||
pity = players[player].pity;
|
||||
|
||||
// SRB2kart
|
||||
|
|
@ -2722,8 +2723,8 @@ void G_PlayerReborn(INT32 player)
|
|||
p->totalring = totalring;
|
||||
|
||||
p->mare = mare;
|
||||
if (bot)
|
||||
p->bot = 1; // reset to AI-controlled
|
||||
p->bot = bot;
|
||||
p->botvars.difficulty = botdifficulty;
|
||||
p->pity = pity;
|
||||
|
||||
// SRB2kart
|
||||
|
|
@ -3174,96 +3175,9 @@ void G_DoReborn(INT32 playernum)
|
|||
player_t *player = &players[playernum];
|
||||
boolean starpost = false;
|
||||
|
||||
/*if (modeattacking) // Not needed for SRB2Kart.
|
||||
{
|
||||
M_EndModeAttackRun();
|
||||
return;
|
||||
}*/
|
||||
|
||||
// Make sure objectplace is OFF when you first start the level!
|
||||
OP_ResetObjectplace();
|
||||
|
||||
if (player->bot && playernum != consoleplayer)
|
||||
{ // Bots respawn next to their master.
|
||||
mobj_t *oldmo = NULL;
|
||||
|
||||
// first dissasociate the corpse
|
||||
if (player->mo)
|
||||
{
|
||||
oldmo = player->mo;
|
||||
// Don't leave your carcass stuck 10-billion feet in the ground!
|
||||
P_RemoveMobj(player->mo);
|
||||
}
|
||||
|
||||
B_RespawnBot(playernum);
|
||||
if (oldmo)
|
||||
G_ChangePlayerReferences(oldmo, players[playernum].mo);
|
||||
}
|
||||
/*else if (countdowntimeup || (!multiplayer && !modeattacking))
|
||||
{
|
||||
// reload the level from scratch
|
||||
if (countdowntimeup)
|
||||
{
|
||||
player->starpostangle = 0;
|
||||
player->starposttime = 0;
|
||||
player->starpostx = 0;
|
||||
player->starposty = 0;
|
||||
player->starpostz = 0;
|
||||
player->starpostnum = 0;
|
||||
}
|
||||
if (!countdowntimeup && (mapheaderinfo[gamemap-1]->levelflags & LF_NORELOAD))
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
player->playerstate = PST_REBORN;
|
||||
|
||||
P_LoadThingsOnly();
|
||||
|
||||
// Do a wipe
|
||||
wipegamestate = -1;
|
||||
|
||||
if (player->starpostnum) // SRB2kart
|
||||
starpost = true;
|
||||
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
if (camera[i].chase)
|
||||
P_ResetCamera(&players[displayplayers[i]], &camera[i]);
|
||||
}
|
||||
|
||||
// clear cmd building stuff
|
||||
memset(gamekeydown, 0, sizeof (gamekeydown));
|
||||
for (i = 0;i < JOYAXISSET; i++)
|
||||
{
|
||||
joyxmove[i] = joyymove[i] = 0;
|
||||
joy2xmove[i] = joy2ymove[i] = 0;
|
||||
joy3xmove[i] = joy3ymove[i] = 0;
|
||||
joy4xmove[i] = joy4ymove[i] = 0;
|
||||
}
|
||||
mousex = mousey = 0;
|
||||
mouse2x = mouse2y = 0;
|
||||
|
||||
// clear hud messages remains (usually from game startup)
|
||||
CON_ClearHUD();
|
||||
|
||||
// Starpost support
|
||||
G_SpawnPlayer(playernum, starpost);
|
||||
|
||||
if (botingame)
|
||||
{ // Bots respawn next to their master.
|
||||
players[displayplayers[1]].playerstate = PST_REBORN;
|
||||
G_SpawnPlayer(displayplayers[1], false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_BLUA
|
||||
LUAh_MapChange(gamemap);
|
||||
#endif
|
||||
G_DoLoadLevel(true);
|
||||
}
|
||||
}*/
|
||||
else
|
||||
{
|
||||
// respawn at the start
|
||||
mobj_t *oldmo = NULL;
|
||||
|
|
@ -4625,9 +4539,6 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar
|
|||
if (savedata.lives > 0)
|
||||
{
|
||||
color = savedata.skincolor;
|
||||
botskin = savedata.botskin;
|
||||
botcolor = savedata.botcolor;
|
||||
botingame = (botskin != 0);
|
||||
}
|
||||
else if (splitscreen != ssplayers)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -55,7 +55,4 @@ extern gamestate_t gamestate;
|
|||
extern UINT8 ultimatemode; // was sk_insane
|
||||
extern gameaction_t gameaction;
|
||||
|
||||
extern boolean botingame;
|
||||
extern UINT8 botskin, botcolor;
|
||||
|
||||
#endif //__G_STATE__
|
||||
|
|
|
|||
|
|
@ -3095,7 +3095,7 @@ static void HU_DrawRankings(void)
|
|||
if (G_RaceGametype())
|
||||
{
|
||||
if (circuitmap)
|
||||
tab[scorelines].count = players[i].laps+1;
|
||||
tab[scorelines].count = players[i].laps;
|
||||
else
|
||||
tab[scorelines].count = players[i].realtime;
|
||||
}
|
||||
|
|
|
|||
27
src/info.c
27
src/info.c
|
|
@ -16534,6 +16534,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_BOTHINT
|
||||
2004, // doomednum
|
||||
S_INVISIBLE, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
100, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
1*FRACUNIT, // radius
|
||||
2*FRACUNIT, // height
|
||||
0, // display offset
|
||||
100, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_RANDOMAUDIENCE
|
||||
1488, // doomednum
|
||||
S_RANDOMAUDIENCE, // spawnstate
|
||||
|
|
|
|||
|
|
@ -4796,6 +4796,8 @@ typedef enum mobj_type
|
|||
MT_WAYPOINT_RISER,
|
||||
MT_WAYPOINT_ANCHOR,
|
||||
|
||||
MT_BOTHINT,
|
||||
|
||||
MT_RANDOMAUDIENCE,
|
||||
|
||||
MT_FLAYM,
|
||||
|
|
|
|||
831
src/k_bot.c
Normal file
831
src/k_bot.c
Normal file
|
|
@ -0,0 +1,831 @@
|
|||
// SONIC ROBO BLAST 2 KART
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2018-2020 by Kart Krew
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file k_bot.c
|
||||
/// \brief Bot logic & ticcmd generation code
|
||||
|
||||
#include "doomdef.h"
|
||||
#include "d_player.h"
|
||||
#include "g_game.h"
|
||||
#include "r_main.h"
|
||||
#include "p_local.h"
|
||||
#include "k_bot.h"
|
||||
#include "lua_hook.h"
|
||||
#include "byteptr.h"
|
||||
#include "d_net.h" // nodetoplayer
|
||||
#include "k_kart.h"
|
||||
#include "z_zone.h"
|
||||
#include "i_system.h"
|
||||
#include "p_maputl.h"
|
||||
#include "d_ticcmd.h"
|
||||
#include "m_random.h"
|
||||
#include "r_things.h" // numskins
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p)
|
||||
{
|
||||
UINT8 buf[3];
|
||||
UINT8 *buf_p = buf;
|
||||
UINT8 newplayernum = *p;
|
||||
|
||||
// search for a free playernum
|
||||
// we can't use playeringame since it is not updated here
|
||||
for (; newplayernum < MAXPLAYERS; newplayernum++)
|
||||
{
|
||||
UINT8 n;
|
||||
|
||||
for (n = 0; n < MAXNETNODES; n++)
|
||||
if (nodetoplayer[n] == newplayernum
|
||||
|| nodetoplayer2[n] == newplayernum
|
||||
|| nodetoplayer3[n] == newplayernum
|
||||
|| nodetoplayer4[n] == newplayernum)
|
||||
break;
|
||||
|
||||
if (n == MAXNETNODES)
|
||||
break;
|
||||
}
|
||||
|
||||
while (playeringame[newplayernum]
|
||||
&& players[newplayernum].bot
|
||||
&& newplayernum < MAXPLAYERS)
|
||||
{
|
||||
newplayernum++;
|
||||
}
|
||||
|
||||
if (newplayernum >= MAXPLAYERS)
|
||||
{
|
||||
*p = newplayernum;
|
||||
return false;
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, newplayernum);
|
||||
|
||||
if (skin > numskins)
|
||||
{
|
||||
skin = numskins;
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, skin);
|
||||
|
||||
if (difficulty < 1)
|
||||
{
|
||||
difficulty = 1;
|
||||
}
|
||||
else if (difficulty > MAXBOTDIFFICULTY)
|
||||
{
|
||||
difficulty = MAXBOTDIFFICULTY;
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, difficulty);
|
||||
|
||||
SendNetXCmd(XD_ADDBOT, buf, buf_p - buf);
|
||||
|
||||
DEBFILE(va("Server added bot %d\n", newplayernum));
|
||||
// use the next free slot (we can't put playeringame[newplayernum] = true here)
|
||||
newplayernum++;
|
||||
|
||||
*p = newplayernum;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_UpdateMatchRaceBots(void)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
void K_UpdateMatchRaceBots(void)
|
||||
{
|
||||
const UINT8 difficulty = cv_kartbot.value;
|
||||
UINT8 pmax = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value);
|
||||
UINT8 numplayers = 0;
|
||||
UINT8 numbots = 0;
|
||||
UINT8 numwaiting = 0;
|
||||
SINT8 wantedbots = 0;
|
||||
boolean skinusable[MAXSKINS];
|
||||
UINT8 i;
|
||||
|
||||
if (!server)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// init usable bot skins list
|
||||
for (i = 0; i < MAXSKINS; i++)
|
||||
{
|
||||
if (i < numskins)
|
||||
{
|
||||
skinusable[i] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
skinusable[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cv_ingamecap.value > 0)
|
||||
{
|
||||
pmax = min(pmax, cv_ingamecap.value);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i])
|
||||
{
|
||||
if (!players[i].spectator)
|
||||
{
|
||||
skinusable[players[i].skin] = false;
|
||||
|
||||
if (players[i].bot)
|
||||
{
|
||||
numbots++;
|
||||
|
||||
// While we're here, we should update bot difficulty to the proper value.
|
||||
players[i].botvars.difficulty = difficulty;
|
||||
}
|
||||
else
|
||||
{
|
||||
numplayers++;
|
||||
}
|
||||
}
|
||||
else if (players[i].pflags & PF_WANTSTOJOIN)
|
||||
{
|
||||
numwaiting++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (difficulty == 0)
|
||||
{
|
||||
wantedbots = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
wantedbots = pmax - numplayers - numwaiting;
|
||||
|
||||
if (wantedbots < 0)
|
||||
{
|
||||
wantedbots = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (numbots < wantedbots)
|
||||
{
|
||||
// We require MORE bots!
|
||||
UINT8 newplayernum = 0;
|
||||
boolean usedallskins = false;
|
||||
|
||||
if (dedicated)
|
||||
{
|
||||
newplayernum = 1;
|
||||
}
|
||||
|
||||
while (numbots < wantedbots)
|
||||
{
|
||||
UINT8 skin = M_RandomKey(numskins);
|
||||
|
||||
if (usedallskins == false)
|
||||
{
|
||||
UINT8 loops = 0;
|
||||
|
||||
while (!skinusable[skin])
|
||||
{
|
||||
if (loops >= numskins)
|
||||
{
|
||||
// no more skins, stick to our first choice
|
||||
usedallskins = true;
|
||||
break;
|
||||
}
|
||||
|
||||
skin++;
|
||||
|
||||
if (skin >= numskins)
|
||||
{
|
||||
skin = 0;
|
||||
}
|
||||
|
||||
loops++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!K_AddBot(skin, difficulty, &newplayernum))
|
||||
{
|
||||
// Not enough player slots to add the bot, break the loop.
|
||||
break;
|
||||
}
|
||||
|
||||
skinusable[skin] = false;
|
||||
numbots++;
|
||||
}
|
||||
}
|
||||
else if (numbots > wantedbots)
|
||||
{
|
||||
UINT8 buf[2];
|
||||
|
||||
i = MAXPLAYERS;
|
||||
|
||||
while (numbots > wantedbots && i > 0)
|
||||
{
|
||||
if (playeringame[i] && players[i].bot)
|
||||
{
|
||||
buf[0] = i;
|
||||
buf[1] = KR_LEAVE;
|
||||
SendNetXCmd(XD_REMOVEPLAYER, &buf, 2);
|
||||
|
||||
numbots--;
|
||||
}
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
// We should have enough bots now :)
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_PlayerUsesBotMovement(player_t *player)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
boolean K_PlayerUsesBotMovement(player_t *player)
|
||||
{
|
||||
if (player->bot || player->exiting)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_BotCanTakeCut(player_t *player)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
boolean K_BotCanTakeCut(player_t *player)
|
||||
{
|
||||
if (!K_ApplyOffroad(player)
|
||||
|| player->kartstuff[k_itemtype] == KITEM_SNEAKER
|
||||
|| player->kartstuff[k_itemtype] == KITEM_ROCKETSNEAKER
|
||||
|| player->kartstuff[k_itemtype] == KITEM_INVINCIBILITY
|
||||
|| player->kartstuff[k_itemtype] == KITEM_HYUDORO)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static UINT32 K_BotRubberbandDistance(player_t *player)
|
||||
|
||||
Calculates the distance away from 1st place that the
|
||||
bot should rubberband to.
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to compare.
|
||||
|
||||
Return:-
|
||||
Distance to add, as an integer.
|
||||
--------------------------------------------------*/
|
||||
static UINT32 K_BotRubberbandDistance(player_t *player)
|
||||
{
|
||||
const UINT32 spacing = 2048;
|
||||
const UINT8 portpriority = player - players;
|
||||
UINT8 pos = 0;
|
||||
UINT8 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (i == portpriority)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (playeringame[i] && players[i].bot)
|
||||
{
|
||||
// First check difficulty levels, then score, then settle it with port priority!
|
||||
if (player->botvars.difficulty < players[i].botvars.difficulty)
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
else if (player->score < players[i].score)
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
else if (i < portpriority)
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (pos * spacing);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
fixed_t K_BotRubberband(player_t *player)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
fixed_t K_BotRubberband(player_t *player)
|
||||
{
|
||||
fixed_t rubberband = FRACUNIT;
|
||||
player_t *firstplace = NULL;
|
||||
UINT8 i;
|
||||
|
||||
if (player->exiting)
|
||||
{
|
||||
return FRACUNIT;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Only rubberband up to players.
|
||||
if (players[i].bot)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (firstplace == NULL || players[i].distancetofinish < firstplace->distancetofinish)
|
||||
{
|
||||
firstplace = &players[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (firstplace != NULL)
|
||||
{
|
||||
const UINT32 wanteddist = firstplace->distancetofinish + K_BotRubberbandDistance(player);
|
||||
const INT32 distdiff = player->distancetofinish - wanteddist;
|
||||
|
||||
if (wanteddist > player->distancetofinish)
|
||||
{
|
||||
// Whoa, you're too far ahead!
|
||||
rubberband += (MAXBOTDIFFICULTY - player->botvars.difficulty) * distdiff;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Catch up to your position!
|
||||
rubberband += (2*player->botvars.difficulty) * distdiff;
|
||||
}
|
||||
}
|
||||
|
||||
if (rubberband > 2*FRACUNIT)
|
||||
{
|
||||
rubberband = 2*FRACUNIT;
|
||||
}
|
||||
else if (rubberband < 7*FRACUNIT/8)
|
||||
{
|
||||
rubberband = 7*FRACUNIT/8;
|
||||
}
|
||||
|
||||
return rubberband;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
fixed_t K_BotTopSpeedRubberband(player_t *player)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
fixed_t K_BotTopSpeedRubberband(player_t *player)
|
||||
{
|
||||
fixed_t rubberband = K_BotRubberband(player);
|
||||
|
||||
if (rubberband < FRACUNIT)
|
||||
{
|
||||
// Never go below your regular top speed
|
||||
rubberband = FRACUNIT;
|
||||
}
|
||||
|
||||
// Only allow you to go faster than your regular top speed if you're facing the right direction
|
||||
if (rubberband > FRACUNIT && player->mo != NULL && player->nextwaypoint != NULL)
|
||||
{
|
||||
const INT16 mindiff = 30;
|
||||
const INT16 maxdiff = 60;
|
||||
INT16 anglediff = 0;
|
||||
fixed_t amt = rubberband - FRACUNIT;
|
||||
angle_t destangle = R_PointToAngle2(
|
||||
player->mo->x, player->mo->y,
|
||||
player->nextwaypoint->mobj->x, player->nextwaypoint->mobj->y
|
||||
);
|
||||
angle_t angle = player->mo->angle - destangle;
|
||||
|
||||
if (angle < ANGLE_180)
|
||||
{
|
||||
anglediff = AngleFixed(angle) >> FRACBITS;
|
||||
}
|
||||
else
|
||||
{
|
||||
anglediff = 360 - (AngleFixed(angle) >> FRACBITS);
|
||||
}
|
||||
|
||||
anglediff = abs(anglediff);
|
||||
|
||||
if (anglediff >= maxdiff)
|
||||
{
|
||||
rubberband = FRACUNIT;
|
||||
}
|
||||
else if (anglediff > mindiff)
|
||||
{
|
||||
amt = (amt * (maxdiff - anglediff)) / mindiff;
|
||||
rubberband = FRACUNIT + amt;
|
||||
}
|
||||
}
|
||||
|
||||
return rubberband;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy)
|
||||
{
|
||||
fixed_t v1toc[2] = {cx - v1x, cy - v1y};
|
||||
fixed_t v1tov2[2] = {v2x - v1x, v2y - v1y};
|
||||
|
||||
fixed_t mag = FixedMul(v1tov2[0], v1tov2[0]) + FixedMul(v1tov2[1], v1tov2[1]);
|
||||
fixed_t dot = FixedMul(v1toc[0], v1tov2[0]) + FixedMul(v1toc[1], v1tov2[1]);
|
||||
|
||||
fixed_t t;
|
||||
fixed_t px, py;
|
||||
|
||||
if (mag == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
t = FixedDiv(dot, mag);
|
||||
|
||||
px = v1x + FixedMul(v1tov2[0], t);
|
||||
py = v1y + FixedMul(v1tov2[1], t);
|
||||
|
||||
return P_AproxDistance(cx - px, cy - py);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static botprediction_t *K_CreateBotPrediction(player_t *player)
|
||||
|
||||
Calculates a point further along the track to attempt to drive towards.
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to compare.
|
||||
|
||||
Return:-
|
||||
Bot prediction struct.
|
||||
--------------------------------------------------*/
|
||||
static botprediction_t *K_CreateBotPrediction(player_t *player)
|
||||
{
|
||||
const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn
|
||||
const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to
|
||||
|
||||
const fixed_t distreduce = K_BotReducePrediction(player);
|
||||
const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT);
|
||||
|
||||
const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict
|
||||
const fixed_t speed = max(P_AproxDistance(player->mo->momx, player->mo->momy), K_GetKartSpeed(player, false) / 4);
|
||||
const INT32 distance = (FixedMul(speed, distreduce) / FRACUNIT) * futuresight;
|
||||
|
||||
botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL);
|
||||
waypoint_t *wp = player->nextwaypoint;
|
||||
|
||||
INT32 distanceleft = distance;
|
||||
fixed_t smallestradius = INT32_MAX;
|
||||
angle_t angletonext = ANGLE_MAX;
|
||||
|
||||
size_t nwp;
|
||||
size_t i;
|
||||
|
||||
// Reduce distance left by your distance to the starting waypoint.
|
||||
// This prevents looking too far ahead if the closest waypoint is really far away.
|
||||
distanceleft -= P_AproxDistance(player->mo->x - wp->mobj->x, player->mo->y - wp->mobj->y) / FRACUNIT;
|
||||
|
||||
// We don't want to look ahead at all, just go to the first waypoint.
|
||||
if (distanceleft <= 0)
|
||||
{
|
||||
predict->x = wp->mobj->x;
|
||||
predict->y = wp->mobj->y;
|
||||
predict->radius = FixedMul(wp->mobj->radius, radreduce);
|
||||
return predict;
|
||||
}
|
||||
|
||||
angletonext = R_PointToAngle2(
|
||||
player->mo->x, player->mo->y,
|
||||
wp->mobj->x, wp->mobj->y
|
||||
);
|
||||
|
||||
// Go through waypoints until we've traveled the distance we wanted to predict ahead!
|
||||
while (distanceleft > 0)
|
||||
{
|
||||
INT32 disttonext = INT32_MAX;
|
||||
|
||||
if (wp->mobj->radius < smallestradius)
|
||||
{
|
||||
smallestradius = wp->mobj->radius;
|
||||
}
|
||||
|
||||
if (wp->numnextwaypoints == 0)
|
||||
{
|
||||
// Well, this is where I get off.
|
||||
distanceleft = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate nextwaypoints index to use
|
||||
// nextwaypoints[0] by default
|
||||
nwp = 0;
|
||||
|
||||
// There are multiple nextwaypoints,
|
||||
// so we need to find the most convenient one to us.
|
||||
// Let's compare the angle to the player's!
|
||||
if (wp->numnextwaypoints > 1)
|
||||
{
|
||||
angle_t delta = ANGLE_MAX;
|
||||
angle_t a = ANGLE_MAX;
|
||||
|
||||
for (i = 0; i < wp->numnextwaypoints; i++)
|
||||
{
|
||||
|
||||
if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) && !K_BotCanTakeCut(player))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unlike the other parts of this function, we're comparing the player's physical position, NOT the position of the waypoint!!
|
||||
// This should roughly correspond with how players will think about path splits.
|
||||
a = R_PointToAngle2(
|
||||
player->mo->x, player->mo->y,
|
||||
wp->nextwaypoints[i]->mobj->x, wp->nextwaypoints[i]->mobj->y
|
||||
);
|
||||
if (a > ANGLE_180)
|
||||
{
|
||||
a = InvAngle(a);
|
||||
}
|
||||
|
||||
a = player->mo->angle - a;
|
||||
|
||||
if (a < delta)
|
||||
{
|
||||
nwp = i;
|
||||
delta = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angletonext = R_PointToAngle2(
|
||||
wp->mobj->x, wp->mobj->y,
|
||||
wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y
|
||||
);
|
||||
|
||||
disttonext = (INT32)wp->nextwaypointdistances[nwp];
|
||||
|
||||
if (disttonext > distanceleft)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
distanceleft -= disttonext;
|
||||
|
||||
wp = wp->nextwaypoints[nwp];
|
||||
}
|
||||
|
||||
// Set our predicted point's coordinates,
|
||||
// and use the smallest radius of all of the waypoints in the chain!
|
||||
predict->x = wp->mobj->x;
|
||||
predict->y = wp->mobj->y;
|
||||
predict->radius = FixedMul(smallestradius, radreduce);
|
||||
|
||||
// Set the prediction coordinates between the 2 waypoints if there's still distance left.
|
||||
if (distanceleft > 0)
|
||||
{
|
||||
// Scaled with the leftover anglemul!
|
||||
predict->x += P_ReturnThrustX(NULL, angletonext, distanceleft * FRACUNIT);
|
||||
predict->y += P_ReturnThrustY(NULL, angletonext, distanceleft * FRACUNIT);
|
||||
}
|
||||
|
||||
return predict;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
|
||||
{
|
||||
botprediction_t *predict = NULL;
|
||||
INT32 turnamt = 0;
|
||||
|
||||
// Can't build a ticcmd if we aren't spawned...
|
||||
if (!player->mo)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any existing controls
|
||||
memset(cmd, 0, sizeof(ticcmd_t));
|
||||
cmd->angleturn = (player->mo->angle >> 16) | TICCMD_RECEIVED;
|
||||
|
||||
if (gamestate != GS_LEVEL)
|
||||
{
|
||||
// No need to do anything else.
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->playerstate == PST_DEAD)
|
||||
{
|
||||
cmd->buttons |= BT_ACCELERATE;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_BLUA
|
||||
// Complete override of all ticcmd functionality
|
||||
if (LUAh_BotTiccmd(player, cmd))
|
||||
return;
|
||||
#endif
|
||||
|
||||
// Start boost handler
|
||||
if (leveltime <= starttime)
|
||||
{
|
||||
tic_t boosthold = starttime - TICRATE;
|
||||
|
||||
boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty);
|
||||
|
||||
if (leveltime >= boosthold)
|
||||
{
|
||||
cmd->buttons |= BT_ACCELERATE;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle steering towards waypoints!
|
||||
if (player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj))
|
||||
{
|
||||
SINT8 turnsign = 0;
|
||||
angle_t destangle, moveangle, angle;
|
||||
INT16 anglediff;
|
||||
|
||||
predict = K_CreateBotPrediction(player);
|
||||
|
||||
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
|
||||
moveangle = player->mo->angle;
|
||||
|
||||
angle = (moveangle - destangle);
|
||||
|
||||
if (angle < ANGLE_180)
|
||||
{
|
||||
turnsign = -1; // Turn right
|
||||
anglediff = AngleFixed(angle)>>FRACBITS;
|
||||
}
|
||||
else
|
||||
{
|
||||
turnsign = 1; // Turn left
|
||||
anglediff = 360-(AngleFixed(angle)>>FRACBITS);
|
||||
}
|
||||
|
||||
anglediff = abs(anglediff);
|
||||
turnamt = KART_FULLTURN * turnsign;
|
||||
|
||||
if (anglediff > 90)
|
||||
{
|
||||
// Wrong way!
|
||||
cmd->forwardmove = -25;
|
||||
cmd->buttons |= BT_BRAKE;
|
||||
}
|
||||
else
|
||||
{
|
||||
const fixed_t playerwidth = (player->mo->radius * 2);
|
||||
const fixed_t realrad = predict->radius - (playerwidth * 4); // Remove a "safe" distance away from the edges of the road
|
||||
fixed_t rad = realrad;
|
||||
fixed_t dirdist = K_DistanceOfLineFromPoint(
|
||||
player->mo->x, player->mo->y,
|
||||
player->mo->x + FINECOSINE(moveangle >> ANGLETOFINESHIFT), player->mo->y + FINESINE(moveangle >> ANGLETOFINESHIFT),
|
||||
predict->x, predict->y
|
||||
);
|
||||
|
||||
if (anglediff > 0)
|
||||
{
|
||||
// Become more precise based on how hard you need to turn
|
||||
// This makes predictions into turns a little nicer
|
||||
// Facing 90 degrees away from the predicted point gives you a 1/3 radius
|
||||
rad = FixedMul(rad, ((135 - anglediff) * FRACUNIT) / 135);
|
||||
}
|
||||
|
||||
if (rad > realrad)
|
||||
{
|
||||
rad = realrad;
|
||||
}
|
||||
else if (rad < playerwidth)
|
||||
{
|
||||
rad = playerwidth;
|
||||
}
|
||||
|
||||
cmd->buttons |= BT_ACCELERATE;
|
||||
|
||||
// Full speed ahead!
|
||||
cmd->forwardmove = 50;
|
||||
|
||||
if (dirdist <= rad)
|
||||
{
|
||||
fixed_t speedmul = FixedDiv(player->speed, K_GetKartSpeed(player, false));
|
||||
fixed_t speedrad = rad/4;
|
||||
|
||||
if (speedmul > FRACUNIT)
|
||||
{
|
||||
speedmul = FRACUNIT;
|
||||
}
|
||||
|
||||
// Increase radius with speed
|
||||
// At low speed, the CPU will try to be more accurate
|
||||
// At high speed, they're more likely to lawnmower
|
||||
speedrad += FixedMul(speedmul, rad - speedrad);
|
||||
|
||||
if (speedrad < playerwidth)
|
||||
{
|
||||
speedrad = playerwidth;
|
||||
}
|
||||
|
||||
if (dirdist <= speedrad)
|
||||
{
|
||||
// Don't turn at all
|
||||
turnamt = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make minor adjustments
|
||||
turnamt /= 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (anglediff > 60)
|
||||
{
|
||||
// Actually, don't go too fast...
|
||||
cmd->forwardmove /= 2;
|
||||
cmd->buttons |= BT_BRAKE;
|
||||
}
|
||||
else if (dirdist <= realrad)
|
||||
{
|
||||
// Steer towards/away from objects!
|
||||
turnamt += K_BotFindObjects(player, turnamt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle item usage
|
||||
K_BotItemUsage(player, cmd, turnamt);
|
||||
|
||||
if (turnamt != 0)
|
||||
{
|
||||
if (turnamt > KART_FULLTURN)
|
||||
{
|
||||
turnamt = KART_FULLTURN;
|
||||
}
|
||||
else if (turnamt < -KART_FULLTURN)
|
||||
{
|
||||
turnamt = -KART_FULLTURN;
|
||||
}
|
||||
|
||||
if (turnamt > 0)
|
||||
{
|
||||
if (player->botvars.turnconfirm < BOTTURNCONFIRM)
|
||||
{
|
||||
player->botvars.turnconfirm++;
|
||||
}
|
||||
}
|
||||
else if (turnamt < 0)
|
||||
{
|
||||
if (player->botvars.turnconfirm > -BOTTURNCONFIRM)
|
||||
{
|
||||
player->botvars.turnconfirm--;
|
||||
}
|
||||
}
|
||||
|
||||
if (abs(player->botvars.turnconfirm) >= BOTTURNCONFIRM)
|
||||
{
|
||||
// You're commiting to your turn, you're allowed!
|
||||
cmd->driftturn = turnamt;
|
||||
cmd->angleturn += K_GetKartTurnValue(player, turnamt);
|
||||
}
|
||||
}
|
||||
|
||||
// Free the prediction we made earlier
|
||||
if (predict != NULL)
|
||||
{
|
||||
Z_Free(predict);
|
||||
}
|
||||
}
|
||||
|
||||
232
src/k_bot.h
Normal file
232
src/k_bot.h
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
// SONIC ROBO BLAST 2 KART
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2018-2020 by Kart Krew
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file k_bot.h
|
||||
/// \brief Bot logic & ticcmd generation code
|
||||
|
||||
#ifndef __K_BOT__
|
||||
#define __K_BOT__
|
||||
|
||||
#include "k_waypoint.h"
|
||||
#include "d_player.h"
|
||||
|
||||
// Maximum value of botvars.difficulty
|
||||
#define MAXBOTDIFFICULTY 9
|
||||
|
||||
// How many tics in a row do you need to turn in this direction before we'll let you turn.
|
||||
// Made it as small as possible without making it look like the bots are twitching constantly.
|
||||
#define BOTTURNCONFIRM 4
|
||||
|
||||
// Point for bots to aim for
|
||||
typedef struct botprediction_s {
|
||||
fixed_t x, y;
|
||||
fixed_t radius;
|
||||
} botprediction_t;
|
||||
|
||||
|
||||
// AVAILABLE FOR LUA
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_PlayerUsesBotMovement(player_t *player);
|
||||
|
||||
Tells if this player is being controlled via bot movement code (is a bot, or is exiting).
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to check.
|
||||
|
||||
Return:-
|
||||
true if using bot movement code, otherwise false.
|
||||
--------------------------------------------------*/
|
||||
|
||||
boolean K_PlayerUsesBotMovement(player_t *player);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_BotCanTakeCut(player_t *player);
|
||||
|
||||
Tells if this bot is able to take shortcuts (currently unaffected by offroad,
|
||||
or has certain items ready).
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to check.
|
||||
|
||||
Return:-
|
||||
true if able to take shortcuts, otherwise false.
|
||||
--------------------------------------------------*/
|
||||
|
||||
boolean K_BotCanTakeCut(player_t *player);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
fixed_t K_BotRubberband(player_t *player);
|
||||
|
||||
Gives a multiplier for a bot's rubberbanding.
|
||||
Meant to be used for acceleration and handling.
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to check.
|
||||
|
||||
Return:-
|
||||
A multiplier in fixed point scale.
|
||||
--------------------------------------------------*/
|
||||
|
||||
fixed_t K_BotRubberband(player_t *player);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
fixed_t K_BotTopSpeedRubberband(player_t *player);
|
||||
|
||||
Gives a multiplier for a bot's rubberbanding.
|
||||
Adjusted from K_BotRubberband to be used for top speed.
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to check.
|
||||
|
||||
Return:-
|
||||
A multiplier in fixed point scale.
|
||||
--------------------------------------------------*/
|
||||
|
||||
fixed_t K_BotTopSpeedRubberband(player_t *player);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy);
|
||||
|
||||
Gets the distance of a point away from a line.
|
||||
TODO: Could go in another file?
|
||||
|
||||
Input Arguments:-
|
||||
v1x - Line's first vertex x position.
|
||||
v1y - Line's first vertex y position.
|
||||
v2x - Line's second vertex x position.
|
||||
v2y - Line's second vertex y position.
|
||||
cx - Point's x position.
|
||||
cy - Point's y position.
|
||||
|
||||
Return:-
|
||||
The distance between the point and the line.
|
||||
--------------------------------------------------*/
|
||||
|
||||
fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy);
|
||||
|
||||
|
||||
// NOT AVAILABLE FOR LUA
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum);
|
||||
|
||||
Returns the waypoint actually being used as the finish line.
|
||||
|
||||
Input Arguments:-
|
||||
skin - Skin number that the bot will use.
|
||||
difficulty - Difficulty level this bot will use.
|
||||
newplayernum - Pointer to the last valid player slot number.
|
||||
Is a pointer so that this function can be called multiple times to add more than one bot.
|
||||
|
||||
Return:-
|
||||
true if a bot packet can be sent, otherwise false.
|
||||
--------------------------------------------------*/
|
||||
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_UpdateMatchRaceBots(void);
|
||||
|
||||
Updates the number of bots in the server and their difficulties for Match Race.
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_UpdateMatchRaceBots(void);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
UINT8 K_EggboxStealth(fixed_t x, fixed_t y);
|
||||
|
||||
Gets a "stealth" value for a position, to figure out how
|
||||
well Eggman boxes blend into random items.
|
||||
|
||||
Input Arguments:-
|
||||
x - X coordinate to check.
|
||||
y - Y coordinate to check.
|
||||
|
||||
Return:-
|
||||
Stealth value for the position.
|
||||
--------------------------------------------------*/
|
||||
|
||||
UINT8 K_EggboxStealth(fixed_t x, fixed_t y);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
fixed_t K_BotReducePrediction(player_t *player);
|
||||
|
||||
Finds walls nearby the specified player, to create a multiplier
|
||||
to pull bot predictions back by.
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to compare.
|
||||
|
||||
Return:-
|
||||
Multiplier in fixed point scale.
|
||||
--------------------------------------------------*/
|
||||
|
||||
fixed_t K_BotReducePrediction(player_t *player);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
INT16 K_BotFindObjects(player_t *player, INT16 turn);
|
||||
|
||||
Generates a sum for objects to steer towards/away from.
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to compare.
|
||||
turn - Turn value before object steering.
|
||||
|
||||
Return:-
|
||||
Turn amount sum to add to final product.
|
||||
--------------------------------------------------*/
|
||||
|
||||
INT16 K_BotFindObjects(player_t *player, INT16 turn);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd);
|
||||
|
||||
Gives a multiplier for a bot's rubberbanding. Meant to be used for top speed,
|
||||
acceleration, and handling.
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to generate the ticcmd for.
|
||||
cmd - The player's ticcmd to modify.
|
||||
|
||||
Return:-
|
||||
None
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt);
|
||||
|
||||
Item usage part of ticcmd generation.
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to generate the ticcmd for.
|
||||
cmd - The player's ticcmd to modify.
|
||||
turnamt - How hard the bot is turning.
|
||||
|
||||
Return:-
|
||||
None
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt);
|
||||
|
||||
|
||||
#endif
|
||||
1095
src/k_botitem.c
Normal file
1095
src/k_botitem.c
Normal file
File diff suppressed because it is too large
Load diff
781
src/k_botsearch.c
Normal file
781
src/k_botsearch.c
Normal file
|
|
@ -0,0 +1,781 @@
|
|||
// SONIC ROBO BLAST 2 KART
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2018-2020 by Kart Krew
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file k_botsearch.c
|
||||
/// \brief Bot blockmap search functions
|
||||
|
||||
#include "doomdef.h"
|
||||
#include "d_player.h"
|
||||
#include "g_game.h"
|
||||
#include "r_main.h"
|
||||
#include "p_local.h"
|
||||
#include "k_bot.h"
|
||||
#include "lua_hook.h"
|
||||
#include "byteptr.h"
|
||||
#include "d_net.h" // nodetoplayer
|
||||
#include "k_kart.h"
|
||||
#include "z_zone.h"
|
||||
#include "i_system.h"
|
||||
#include "p_maputl.h"
|
||||
#include "d_ticcmd.h"
|
||||
#include "m_random.h"
|
||||
#include "r_things.h" // numskins
|
||||
#include "p_slopes.h" // P_GetZAt
|
||||
|
||||
struct globalsmuggle
|
||||
{
|
||||
mobj_t *botmo;
|
||||
fixed_t distancetocheck;
|
||||
|
||||
fixed_t closestlinedist;
|
||||
|
||||
INT16 curturn;
|
||||
INT16 steer;
|
||||
|
||||
fixed_t eggboxx, eggboxy;
|
||||
UINT8 randomitems;
|
||||
UINT8 eggboxes;
|
||||
} globalsmuggle;
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_FindEggboxes(mobj_t *thing)
|
||||
|
||||
Blockmap search function.
|
||||
Increments the random items and egg boxes counters.
|
||||
|
||||
Input Arguments:-
|
||||
thing - Object passed in from iteration.
|
||||
|
||||
Return:-
|
||||
true continues searching, false ends the search early.
|
||||
--------------------------------------------------*/
|
||||
static boolean K_FindEggboxes(mobj_t *thing)
|
||||
{
|
||||
fixed_t dist;
|
||||
|
||||
if (thing->type != MT_RANDOMITEM && thing->type != MT_EGGMANITEM)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!thing->health)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
dist = P_AproxDistance(thing->x - globalsmuggle.eggboxx, thing->y - globalsmuggle.eggboxy);
|
||||
|
||||
if (dist > globalsmuggle.distancetocheck)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thing->type == MT_RANDOMITEM)
|
||||
{
|
||||
globalsmuggle.randomitems++;
|
||||
}
|
||||
else
|
||||
{
|
||||
globalsmuggle.eggboxes++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
UINT8 K_EggboxStealth(fixed_t x, fixed_t y)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
UINT8 K_EggboxStealth(fixed_t x, fixed_t y)
|
||||
{
|
||||
INT32 xl, xh, yl, yh, bx, by;
|
||||
|
||||
globalsmuggle.eggboxx = x;
|
||||
globalsmuggle.eggboxy = y;
|
||||
globalsmuggle.distancetocheck = (mapobjectscale * 256);
|
||||
globalsmuggle.randomitems = 0;
|
||||
globalsmuggle.eggboxes = 0;
|
||||
|
||||
xl = (unsigned)(globalsmuggle.eggboxx - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
|
||||
xh = (unsigned)(globalsmuggle.eggboxx + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
|
||||
yl = (unsigned)(globalsmuggle.eggboxy - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
|
||||
yh = (unsigned)(globalsmuggle.eggboxy + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
|
||||
|
||||
BMBOUNDFIX(xl, xh, yl, yh);
|
||||
|
||||
for (bx = xl; bx <= xh; bx++)
|
||||
{
|
||||
for (by = yl; by <= yh; by++)
|
||||
{
|
||||
P_BlockThingsIterator(bx, by, K_FindEggboxes);
|
||||
}
|
||||
}
|
||||
|
||||
return (globalsmuggle.randomitems * globalsmuggle.eggboxes);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec)
|
||||
|
||||
Tells us if a bot will play more careful around
|
||||
this sector's special type.
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to check against.
|
||||
sec - Sector to check the specials of.
|
||||
|
||||
Return:-
|
||||
true if avoiding this sector special, false otherwise.
|
||||
--------------------------------------------------*/
|
||||
static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec)
|
||||
{
|
||||
switch (GETSECSPECIAL(sec->special, 1))
|
||||
{
|
||||
case 1: // Damage
|
||||
case 5: // Spikes
|
||||
case 6: case 7: // Death Pit
|
||||
case 8: // Instant Kill
|
||||
return true;
|
||||
//case 2: case 3: // Offroad (let's let them lawnmower)
|
||||
case 4: // Offroad (Strong)
|
||||
if (!K_BotCanTakeCut(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
|
||||
|
||||
Tells us if a bot will play more careful around
|
||||
this sector. Checks FOFs in the sector, as well.
|
||||
|
||||
Input Arguments:-
|
||||
player - Player to check against.
|
||||
sec - Sector to check against.
|
||||
x - Linedef cross X position, for slopes
|
||||
y - Linedef cross Y position, for slopes
|
||||
|
||||
Return:-
|
||||
true if avoiding this sector, false otherwise.
|
||||
--------------------------------------------------*/
|
||||
static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
|
||||
{
|
||||
const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP);
|
||||
INT32 specialflag = 0;
|
||||
fixed_t highestfloor = INT32_MAX;
|
||||
sector_t *bestsector = NULL;
|
||||
ffloor_t *rover;
|
||||
|
||||
if (flip == true)
|
||||
{
|
||||
specialflag = SF_FLIPSPECIAL_CEILING;
|
||||
highestfloor = (sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : sec->ceilingheight);
|
||||
}
|
||||
else
|
||||
{
|
||||
specialflag = SF_FLIPSPECIAL_FLOOR;
|
||||
highestfloor = (sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : sec->floorheight);
|
||||
}
|
||||
|
||||
if (sec->flags & specialflag)
|
||||
{
|
||||
bestsector = sec;
|
||||
}
|
||||
|
||||
for (rover = sec->ffloors; rover; rover = rover->next)
|
||||
{
|
||||
fixed_t top = INT32_MAX;
|
||||
fixed_t bottom = INT32_MAX;
|
||||
|
||||
if (!(rover->flags & FF_EXISTS))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
top = (*rover->t_slope ? P_GetZAt(*rover->t_slope, x, y) : *rover->topheight);
|
||||
bottom = (*rover->b_slope ? P_GetZAt(*rover->b_slope, x, y) : *rover->bottomheight);
|
||||
|
||||
if (!(rover->flags & FF_BLOCKPLAYER))
|
||||
{
|
||||
if ((top >= player->mo->z) && (bottom <= player->mo->z + player->mo->height)
|
||||
&& K_BotHatesThisSectorsSpecial(player, rover->master->frontsector))
|
||||
{
|
||||
// Bad intangible sector at our height, so we DEFINITELY want to avoid
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((rover->flags & FF_BLOCKPLAYER) && !(rover->master->frontsector->flags & specialflag))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the highest FOF floor beneath the player, and check it at the end.
|
||||
if (flip == true)
|
||||
{
|
||||
if (bottom < highestfloor
|
||||
&& bottom >= player->mo->z + player->mo->height)
|
||||
{
|
||||
bestsector = rover->master->frontsector;
|
||||
highestfloor = bottom;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (top > highestfloor
|
||||
&& top <= player->mo->z)
|
||||
{
|
||||
bestsector = rover->master->frontsector;
|
||||
highestfloor = top;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestsector == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return K_BotHatesThisSectorsSpecial(player, bestsector);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_FindBlockingWalls(line_t *line)
|
||||
|
||||
Blockmap search function.
|
||||
Reels the bot prediction back in based on solid walls
|
||||
or other obstacles surrounding the bot.
|
||||
|
||||
Input Arguments:-
|
||||
line - Linedef passed in from iteration.
|
||||
|
||||
Return:-
|
||||
true continues searching, false ends the search early.
|
||||
--------------------------------------------------*/
|
||||
static boolean K_FindBlockingWalls(line_t *line)
|
||||
{
|
||||
// Condensed version of PIT_CheckLine
|
||||
const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale);
|
||||
fixed_t maxstep = maxstepmove;
|
||||
fixed_t linedist = INT32_MAX;
|
||||
INT32 lineside = 0;
|
||||
vertex_t pos;
|
||||
|
||||
if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (line->polyobj && !(line->polyobj->flags & POF_SOLID))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tmbbox[BOXRIGHT] <= line->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= line->bbox[BOXRIGHT]
|
||||
|| tmbbox[BOXTOP] <= line->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= line->bbox[BOXTOP])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (P_BoxOnLineSide(tmbbox, line) != -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
lineside = P_PointOnLineSide(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line);
|
||||
|
||||
// one sided line
|
||||
if (!line->backsector)
|
||||
{
|
||||
if (lineside)
|
||||
{
|
||||
// don't hit the back side
|
||||
return true;
|
||||
}
|
||||
|
||||
goto blocked;
|
||||
}
|
||||
|
||||
if ((line->flags & ML_IMPASSABLE) || (line->flags & ML_BLOCKPLAYERS))
|
||||
{
|
||||
goto blocked;
|
||||
}
|
||||
|
||||
// set openrange, opentop, openbottom
|
||||
P_LineOpening(line, globalsmuggle.botmo);
|
||||
|
||||
if (globalsmuggle.botmo->player->kartstuff[k_waterskip])
|
||||
maxstep += maxstepmove;
|
||||
|
||||
if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 13, false))
|
||||
maxstep <<= 1;
|
||||
else if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 12, false))
|
||||
maxstep = 0;
|
||||
|
||||
if ((openrange < globalsmuggle.botmo->height) // doesn't fit
|
||||
|| (opentop - globalsmuggle.botmo->z < globalsmuggle.botmo->height) // mobj is too high
|
||||
|| (openbottom - globalsmuggle.botmo->z > maxstep)) // too big a step up
|
||||
{
|
||||
goto blocked;
|
||||
}
|
||||
|
||||
// Treat damage sectors like walls
|
||||
P_ClosestPointOnLine(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line, &pos);
|
||||
|
||||
if (lineside)
|
||||
{
|
||||
if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->frontsector, pos.x, pos.y))
|
||||
goto blocked;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->backsector, pos.x, pos.y))
|
||||
goto blocked;
|
||||
}
|
||||
|
||||
// We weren't blocked!
|
||||
return true;
|
||||
|
||||
blocked:
|
||||
linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, globalsmuggle.botmo->x, globalsmuggle.botmo->y);
|
||||
linedist -= (globalsmuggle.botmo->radius * 8); // Maintain a reasonable distance away from it
|
||||
|
||||
if (linedist > globalsmuggle.distancetocheck)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (linedist <= 0)
|
||||
{
|
||||
globalsmuggle.closestlinedist = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (linedist < globalsmuggle.closestlinedist)
|
||||
{
|
||||
globalsmuggle.closestlinedist = linedist;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
fixed_t K_BotReducePrediction(player_t *player)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
fixed_t K_BotReducePrediction(player_t *player)
|
||||
{
|
||||
INT32 xl, xh, yl, yh, bx, by;
|
||||
|
||||
globalsmuggle.botmo = player->mo;
|
||||
globalsmuggle.distancetocheck = (player->mo->radius * 16);
|
||||
globalsmuggle.closestlinedist = INT32_MAX;
|
||||
|
||||
tmx = player->mo->x;
|
||||
tmy = player->mo->y;
|
||||
|
||||
xl = (unsigned)(tmx - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
|
||||
xh = (unsigned)(tmx + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
|
||||
yl = (unsigned)(tmy - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
|
||||
yh = (unsigned)(tmy + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
|
||||
|
||||
BMBOUNDFIX(xl, xh, yl, yh);
|
||||
|
||||
tmbbox[BOXTOP] = tmy + globalsmuggle.distancetocheck;
|
||||
tmbbox[BOXBOTTOM] = tmy - globalsmuggle.distancetocheck;
|
||||
tmbbox[BOXRIGHT] = tmx + globalsmuggle.distancetocheck;
|
||||
tmbbox[BOXLEFT] = tmx - globalsmuggle.distancetocheck;
|
||||
|
||||
// Check for lines that the bot might collide with
|
||||
for (bx = xl; bx <= xh; bx++)
|
||||
{
|
||||
for (by = yl; by <= yh; by++)
|
||||
{
|
||||
P_BlockLinesIterator(bx, by, K_FindBlockingWalls);
|
||||
}
|
||||
}
|
||||
|
||||
if (globalsmuggle.closestlinedist == INT32_MAX)
|
||||
{
|
||||
return FRACUNIT;
|
||||
}
|
||||
|
||||
return FixedDiv(globalsmuggle.closestlinedist, globalsmuggle.distancetocheck);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount)
|
||||
|
||||
Handles steering away/towards the specified object.
|
||||
|
||||
Input Arguments:-
|
||||
bot - Bot's mobj.
|
||||
thing - Mobj to steer towards/away from.
|
||||
fulldist - Distance away from object.
|
||||
xdist - Horizontal distance away from object.
|
||||
towards - If true, steer towards the object. Otherwise, steer away.
|
||||
amount - How hard to turn.
|
||||
|
||||
Return:-
|
||||
None
|
||||
--------------------------------------------------*/
|
||||
static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount)
|
||||
{
|
||||
angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y);
|
||||
angle_t angle;
|
||||
SINT8 flip = 1;
|
||||
|
||||
amount = (amount * FixedDiv(globalsmuggle.distancetocheck - fulldist, globalsmuggle.distancetocheck)) / FRACUNIT;
|
||||
|
||||
if (amount == 0)
|
||||
{
|
||||
// Shouldn't happen
|
||||
return;
|
||||
}
|
||||
|
||||
if (towards)
|
||||
{
|
||||
if (xdist < FixedHypot(bot->radius, thing->radius))
|
||||
{
|
||||
// Don't need to turn any harder!
|
||||
|
||||
if (abs(globalsmuggle.steer) <= amount)
|
||||
{
|
||||
globalsmuggle.steer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (globalsmuggle.steer > 0)
|
||||
{
|
||||
globalsmuggle.steer -= amount;
|
||||
}
|
||||
else if (globalsmuggle.steer < 0)
|
||||
{
|
||||
globalsmuggle.steer += amount;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Still turning towards it, flip.
|
||||
flip = -flip;
|
||||
}
|
||||
|
||||
angle = (bot->angle - destangle);
|
||||
if (angle < ANGLE_180)
|
||||
{
|
||||
flip = -flip;
|
||||
}
|
||||
|
||||
// If going in the opposite direction of where you wanted to turn,
|
||||
// then reduce the amount that you can turn in that direction.
|
||||
if ((flip == 1 && globalsmuggle.curturn < 0)
|
||||
|| (flip == -1 && globalsmuggle.curturn > 0))
|
||||
{
|
||||
amount /= 4;
|
||||
}
|
||||
|
||||
globalsmuggle.steer += amount * flip;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_BotSteerObjects(mobj_t *thing)
|
||||
|
||||
Blockmap search function.
|
||||
Finds objects around the bot to steer towards/away from.
|
||||
|
||||
Input Arguments:-
|
||||
thing - Object passed in from iteration.
|
||||
|
||||
Return:-
|
||||
true continues searching, false ends the search early.
|
||||
--------------------------------------------------*/
|
||||
static boolean K_BotSteerObjects(mobj_t *thing)
|
||||
{
|
||||
INT16 anglediff;
|
||||
fixed_t xdist, ydist, fulldist;
|
||||
angle_t destangle, angle;
|
||||
INT16 attack = ((9 - globalsmuggle.botmo->player->kartspeed) * KART_FULLTURN) / 8; // Acceleration chars are more aggressive
|
||||
INT16 dodge = ((9 - globalsmuggle.botmo->player->kartweight) * KART_FULLTURN) / 8; // Handling chars dodge better
|
||||
|
||||
if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!thing->health)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (globalsmuggle.botmo == thing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
xdist = K_DistanceOfLineFromPoint(
|
||||
globalsmuggle.botmo->x, globalsmuggle.botmo->y,
|
||||
globalsmuggle.botmo->x + FINECOSINE(globalsmuggle.botmo->angle >> ANGLETOFINESHIFT), globalsmuggle.botmo->y + FINESINE(globalsmuggle.botmo->angle >> ANGLETOFINESHIFT),
|
||||
thing->x, thing->y
|
||||
) / 2; // weight x dist more heavily than y dist
|
||||
|
||||
ydist = K_DistanceOfLineFromPoint(
|
||||
globalsmuggle.botmo->x, globalsmuggle.botmo->y,
|
||||
globalsmuggle.botmo->x + FINECOSINE((globalsmuggle.botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), globalsmuggle.botmo->y + FINESINE((globalsmuggle.botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT),
|
||||
thing->x, thing->y
|
||||
);
|
||||
|
||||
fulldist = FixedHypot(xdist, ydist);
|
||||
|
||||
if (fulldist > globalsmuggle.distancetocheck)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!P_CheckSight(globalsmuggle.botmo, thing))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
destangle = R_PointToAngle2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, thing->x, thing->y);
|
||||
angle = (globalsmuggle.botmo->angle - destangle);
|
||||
|
||||
if (angle < ANGLE_180)
|
||||
{
|
||||
anglediff = AngleFixed(angle)>>FRACBITS;
|
||||
}
|
||||
else
|
||||
{
|
||||
anglediff = 360-(AngleFixed(angle)>>FRACBITS);
|
||||
}
|
||||
|
||||
anglediff = abs(anglediff);
|
||||
|
||||
#define PlayerAttackSteer(botcond, thingcond) \
|
||||
if ((botcond) && !(thingcond)) \
|
||||
{ \
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); \
|
||||
} \
|
||||
else if ((thingcond) && !(botcond)) \
|
||||
{ \
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); \
|
||||
}
|
||||
|
||||
switch (thing->type)
|
||||
{
|
||||
case MT_BANANA:
|
||||
case MT_BANANA_SHIELD:
|
||||
case MT_EGGMANITEM_SHIELD:
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
case MT_JAWZ:
|
||||
case MT_JAWZ_DUD:
|
||||
case MT_JAWZ_SHIELD:
|
||||
case MT_SSMINE:
|
||||
case MT_SSMINE_SHIELD:
|
||||
case MT_BALLHOG:
|
||||
case MT_SPB:
|
||||
case MT_BUBBLESHIELDTRAP:
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge));
|
||||
break;
|
||||
case MT_RANDOMITEM:
|
||||
if (anglediff >= 60)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (P_CanPickupItem(globalsmuggle.botmo->player, 1))
|
||||
{
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack);
|
||||
}
|
||||
break;
|
||||
case MT_EGGMANITEM:
|
||||
if (anglediff >= 60)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (P_CanPickupItem(globalsmuggle.botmo->player, 1)) // Can pick up an actual item
|
||||
{
|
||||
const UINT8 stealth = K_EggboxStealth(thing->x, thing->y);
|
||||
const UINT8 requiredstealth = (globalsmuggle.botmo->player->botvars.difficulty * globalsmuggle.botmo->player->botvars.difficulty);
|
||||
|
||||
if (stealth >= requiredstealth)
|
||||
{
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack));
|
||||
}
|
||||
else
|
||||
{
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MT_FLOATINGITEM:
|
||||
if (anglediff >= 60)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (P_CanPickupItem(globalsmuggle.botmo->player, 3))
|
||||
{
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack);
|
||||
}
|
||||
break;
|
||||
case MT_RING:
|
||||
case MT_FLINGRING:
|
||||
if (anglediff >= 60)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if ((RINGTOTAL(globalsmuggle.botmo->player) < 20 && !globalsmuggle.botmo->player->kartstuff[k_ringlock]
|
||||
&& P_CanPickupItem(globalsmuggle.botmo->player, 0))
|
||||
&& !thing->extravalue1
|
||||
&& (globalsmuggle.botmo->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD))
|
||||
{
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true,
|
||||
(RINGTOTAL(globalsmuggle.botmo->player) < 3
|
||||
? (4 * (KART_FULLTURN + attack))
|
||||
: (KART_FULLTURN + attack))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case MT_PLAYER:
|
||||
if (thing->player
|
||||
&& !thing->player->kartstuff[k_hyudorotimer]
|
||||
&& !globalsmuggle.botmo->player->kartstuff[k_hyudorotimer])
|
||||
{
|
||||
// There REALLY ought to be a better way to handle this logic, right?!
|
||||
// Squishing
|
||||
PlayerAttackSteer(
|
||||
globalsmuggle.botmo->scale > thing->scale + (mapobjectscale/8),
|
||||
thing->scale > globalsmuggle.botmo->scale + (mapobjectscale/8)
|
||||
)
|
||||
// Invincibility
|
||||
else PlayerAttackSteer(
|
||||
globalsmuggle.botmo->player->kartstuff[k_invincibilitytimer],
|
||||
thing->player->kartstuff[k_invincibilitytimer]
|
||||
)
|
||||
// Thunder Shield
|
||||
else PlayerAttackSteer(
|
||||
globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD,
|
||||
thing->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD
|
||||
)
|
||||
// Bubble Shield
|
||||
else PlayerAttackSteer(
|
||||
globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD,
|
||||
thing->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD
|
||||
)
|
||||
// Flame Shield
|
||||
else PlayerAttackSteer(
|
||||
globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD,
|
||||
thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD
|
||||
)
|
||||
// Has held item shield
|
||||
else PlayerAttackSteer(
|
||||
(globalsmuggle.botmo->player->kartstuff[k_itemheld] || globalsmuggle.botmo->player->kartstuff[k_eggmanheld]),
|
||||
(thing->player->kartstuff[k_itemheld] || thing->player->kartstuff[k_eggmanheld])
|
||||
)
|
||||
// Ring Sting
|
||||
else PlayerAttackSteer(
|
||||
thing->player->kartstuff[k_rings] <= 0,
|
||||
globalsmuggle.botmo->player->kartstuff[k_rings] <= 0
|
||||
)
|
||||
else
|
||||
{
|
||||
// After ALL of that, we can do standard bumping
|
||||
fixed_t ourweight = K_GetMobjWeight(globalsmuggle.botmo, thing);
|
||||
fixed_t theirweight = K_GetMobjWeight(thing, globalsmuggle.botmo);
|
||||
fixed_t weightdiff = 0;
|
||||
|
||||
if (anglediff >= 90)
|
||||
{
|
||||
weightdiff = theirweight - ourweight;
|
||||
}
|
||||
else
|
||||
{
|
||||
weightdiff = ourweight - theirweight;
|
||||
}
|
||||
|
||||
if (weightdiff > mapobjectscale)
|
||||
{
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack);
|
||||
}
|
||||
else
|
||||
{
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, KART_FULLTURN + dodge);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MT_BOTHINT:
|
||||
if (anglediff >= 60)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (thing->extravalue1 == 0)
|
||||
{
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, thing->extravalue2 * (KART_FULLTURN + dodge));
|
||||
}
|
||||
{
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, thing->extravalue2 * (KART_FULLTURN + attack));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE))
|
||||
{
|
||||
K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
INT16 K_BotFindObjects(player_t *player, INT16 turn)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
INT16 K_BotFindObjects(player_t *player, INT16 turn)
|
||||
{
|
||||
INT32 xl, xh, yl, yh, bx, by;
|
||||
|
||||
globalsmuggle.steer = 0;
|
||||
globalsmuggle.botmo = player->mo;
|
||||
globalsmuggle.curturn = turn;
|
||||
globalsmuggle.distancetocheck = (player->mo->radius * 32) + (player->speed * 4);
|
||||
|
||||
xl = (unsigned)(globalsmuggle.botmo->x - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
|
||||
xh = (unsigned)(globalsmuggle.botmo->x + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
|
||||
yl = (unsigned)(globalsmuggle.botmo->y - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
|
||||
yh = (unsigned)(globalsmuggle.botmo->y + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
|
||||
|
||||
BMBOUNDFIX(xl, xh, yl, yh);
|
||||
|
||||
for (bx = xl; bx <= xh; bx++)
|
||||
{
|
||||
for (by = yl; by <= yh; by++)
|
||||
{
|
||||
P_BlockThingsIterator(bx, by, K_BotSteerObjects);
|
||||
}
|
||||
}
|
||||
|
||||
return globalsmuggle.steer;
|
||||
}
|
||||
252
src/k_kart.c
252
src/k_kart.c
|
|
@ -28,6 +28,7 @@
|
|||
#include "lua_hook.h" // For MobjDamage and ShouldDamage
|
||||
|
||||
#include "k_waypoint.h"
|
||||
#include "k_bot.h"
|
||||
|
||||
// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H:
|
||||
// gamespeed is cc (0 for easy, 1 for normal, 2 for hard)
|
||||
|
|
@ -118,6 +119,7 @@ void K_RegisterKartStuff(void)
|
|||
CV_RegisterVar(&cv_kartvoterulechanges);
|
||||
CV_RegisterVar(&cv_kartspeedometer);
|
||||
CV_RegisterVar(&cv_kartvoices);
|
||||
CV_RegisterVar(&cv_kartbot);
|
||||
CV_RegisterVar(&cv_karteliminatelast);
|
||||
CV_RegisterVar(&cv_kartusepwrlv);
|
||||
CV_RegisterVar(&cv_votetime);
|
||||
|
|
@ -297,6 +299,9 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem)
|
|||
if (getitem == KITEM_HYUDORO) // Hyudoro cooldown
|
||||
hyubgone = 5*TICRATE;
|
||||
|
||||
player->botvars.itemdelay = TICRATE;
|
||||
player->botvars.itemconfirm = 0;
|
||||
|
||||
switch (getitem)
|
||||
{
|
||||
// Special roulettes first, then the generic ones are handled by default
|
||||
|
|
@ -345,7 +350,7 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem)
|
|||
\return void
|
||||
*/
|
||||
|
||||
static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush)
|
||||
static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot)
|
||||
{
|
||||
INT32 newodds;
|
||||
INT32 i;
|
||||
|
|
@ -431,6 +436,46 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp
|
|||
|
||||
#define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime)
|
||||
|
||||
/*
|
||||
if (bot)
|
||||
{
|
||||
// TODO: Item use on bots should all be passed-in functions.
|
||||
// Instead of manually inserting these, it should return 0
|
||||
// for any items without an item use function supplied
|
||||
|
||||
switch (item)
|
||||
{
|
||||
case KITEM_SNEAKER:
|
||||
case KITEM_ROCKETSNEAKER:
|
||||
case KITEM_INVINCIBILITY:
|
||||
case KITEM_BANANA:
|
||||
case KITEM_EGGMAN:
|
||||
case KITEM_ORBINAUT:
|
||||
case KITEM_JAWZ:
|
||||
case KITEM_MINE:
|
||||
case KITEM_BALLHOG:
|
||||
case KITEM_SPB:
|
||||
case KITEM_GROW:
|
||||
case KITEM_SHRINK:
|
||||
case KITEM_HYUDORO:
|
||||
case KITEM_SUPERRING:
|
||||
case KITEM_THUNDERSHIELD:
|
||||
case KITEM_BUBBLESHIELD:
|
||||
case KITEM_FLAMESHIELD:
|
||||
case KRITEM_TRIPLESNEAKER:
|
||||
case KRITEM_TRIPLEBANANA:
|
||||
case KRITEM_TENFOLDBANANA:
|
||||
case KRITEM_TRIPLEORBINAUT:
|
||||
case KRITEM_QUADORBINAUT:
|
||||
case KRITEM_DUALJAWZ:
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
(void)bot;
|
||||
|
||||
switch (item)
|
||||
{
|
||||
case KITEM_ROCKETSNEAKER:
|
||||
|
|
@ -522,7 +567,7 @@ static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8
|
|||
|
||||
for (j = 1; j < NUMKARTRESULTS; j++)
|
||||
{
|
||||
if (K_KartGetItemOdds(i, j, mashed, spbrush) > 0)
|
||||
if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot) > 0)
|
||||
{
|
||||
available = true;
|
||||
break;
|
||||
|
|
@ -820,7 +865,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
|
|||
useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush);
|
||||
|
||||
for (i = 1; i < NUMKARTRESULTS; i++)
|
||||
spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush));
|
||||
spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot));
|
||||
|
||||
// Award the player whatever power is rolled
|
||||
if (totalspawnchance > 0)
|
||||
|
|
@ -875,7 +920,7 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against)
|
|||
return weight;
|
||||
}
|
||||
|
||||
static fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against)
|
||||
fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against)
|
||||
{
|
||||
fixed_t weight = 5*FRACUNIT;
|
||||
|
||||
|
|
@ -2192,6 +2237,13 @@ void K_MomentumToFacing(player_t *player)
|
|||
player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy;
|
||||
}
|
||||
|
||||
boolean K_ApplyOffroad(player_t *player)
|
||||
{
|
||||
if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || EITHERSNEAKER(player))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static fixed_t K_FlameShieldDashVar(INT32 val)
|
||||
{
|
||||
// 1 second = 75% + 50% top speed
|
||||
|
|
@ -2212,8 +2264,7 @@ static void K_GetKartBoostPower(player_t *player)
|
|||
}
|
||||
|
||||
// Offroad is separate, it's difficult to factor it in with a variable value anyway.
|
||||
if (!(player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || EITHERSNEAKER(player))
|
||||
&& player->kartstuff[k_offroad] >= 0)
|
||||
if (K_ApplyOffroad(player) && player->kartstuff[k_offroad] >= 0)
|
||||
boostpower = FixedDiv(boostpower, FixedMul(player->kartstuff[k_offroad], K_GetKartGameSpeedScalar(gamespeed)) + FRACUNIT);
|
||||
|
||||
if (player->kartstuff[k_bananadrag] > TICRATE)
|
||||
|
|
@ -2292,11 +2343,26 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower)
|
|||
|
||||
finalspeed = K_GetKartSpeedFromStat(kartspeed);
|
||||
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
// Give top speed a buff for bots, since it's a fairly weak stat without drifting
|
||||
fixed_t speedmul = ((kartspeed-1) * FRACUNIT / 8) / 10; // +10% for speed 9
|
||||
finalspeed = FixedMul(finalspeed, FRACUNIT + speedmul);
|
||||
}
|
||||
|
||||
if (player->mo && !P_MobjWasRemoved(player->mo))
|
||||
finalspeed = FixedMul(finalspeed, player->mo->scale);
|
||||
|
||||
if (doboostpower)
|
||||
{
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
finalspeed = FixedMul(finalspeed, K_BotTopSpeedRubberband(player));
|
||||
}
|
||||
|
||||
return FixedMul(finalspeed, player->kartstuff[k_boostpower]+player->kartstuff[k_speedboost]);
|
||||
}
|
||||
|
||||
return finalspeed;
|
||||
}
|
||||
|
||||
|
|
@ -2311,6 +2377,11 @@ fixed_t K_GetKartAccel(player_t *player)
|
|||
//k_accel += 3 * (9 - kartspeed); // 36 - 60
|
||||
k_accel += 4 * (9 - kartspeed); // 32 - 64
|
||||
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
k_accel = FixedMul(k_accel, K_BotRubberband(player));
|
||||
}
|
||||
|
||||
return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]);
|
||||
}
|
||||
|
||||
|
|
@ -2365,6 +2436,9 @@ fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove
|
|||
// 0 with no gas, and
|
||||
// -25 when only braking.
|
||||
|
||||
if (EITHERSNEAKER(player))
|
||||
forwardmove = 50;
|
||||
|
||||
finalspeed *= forwardmove/25;
|
||||
finalspeed /= 2;
|
||||
|
||||
|
|
@ -5185,7 +5259,7 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd)
|
|||
class = s+(3*w);
|
||||
|
||||
// Silence the engines
|
||||
if (leveltime < 8 || player->spectator || player->exiting)
|
||||
if (leveltime < 8 || player->spectator)
|
||||
{
|
||||
player->karthud[khud_enginesnd] = 0; // Reset sound number
|
||||
return;
|
||||
|
|
@ -5911,7 +5985,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
player->kartstuff[k_bubblecool] = 0;
|
||||
}
|
||||
|
||||
if (player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD || player->exiting)
|
||||
if (player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD)
|
||||
{
|
||||
if (player->kartstuff[k_flamedash])
|
||||
K_FlameDashLeftoverSmoke(player->mo);
|
||||
|
|
@ -6086,10 +6160,31 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player)
|
|||
angle_t nextbestmomdelta = momdelta;
|
||||
size_t i = 0U;
|
||||
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
// Try to force bots to use a next waypoint
|
||||
nextbestdelta = ANGLE_MAX;
|
||||
nextbestmomdelta = ANGLE_MAX;
|
||||
}
|
||||
|
||||
if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U))
|
||||
{
|
||||
for (i = 0U; i < waypoint->numnextwaypoints; i++)
|
||||
{
|
||||
if (K_PlayerUsesBotMovement(player) == true
|
||||
&& K_GetWaypointIsShortcut(waypoint->nextwaypoints[i]) == true
|
||||
&& K_BotCanTakeCut(player) == false)
|
||||
{
|
||||
// Bots that aren't able to take a shortcut will ignore shortcut waypoints.
|
||||
// (However, if they're already on a shortcut, then we want them to keep going.)
|
||||
|
||||
if (player->nextwaypoint == NULL
|
||||
|| K_GetWaypointIsShortcut(player->nextwaypoint) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
angletowaypoint = R_PointToAngle2(
|
||||
player->mo->x, player->mo->y,
|
||||
waypoint->nextwaypoints[i]->mobj->x, waypoint->nextwaypoints[i]->mobj->y);
|
||||
|
|
@ -6126,7 +6221,8 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player)
|
|||
}
|
||||
}
|
||||
|
||||
if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U))
|
||||
if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U)
|
||||
&& !(K_PlayerUsesBotMovement(player))) // Bots do not need prev waypoints
|
||||
{
|
||||
for (i = 0U; i < waypoint->numprevwaypoints; i++)
|
||||
{
|
||||
|
|
@ -6281,70 +6377,67 @@ void K_UpdateDistanceFromFinishLine(player_t *const player)
|
|||
{
|
||||
if ((player != NULL) && (player->mo != NULL))
|
||||
{
|
||||
waypoint_t *finishline = K_GetFinishLineWaypoint();
|
||||
waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player);
|
||||
|
||||
if (nextwaypoint != NULL)
|
||||
{
|
||||
// If nextwaypoint is NULL, it means we don't want to update the waypoint until we touch another one.
|
||||
// player->nextwaypoint will keep its previous value in this case.
|
||||
player->nextwaypoint = nextwaypoint;
|
||||
}
|
||||
|
||||
// nextwaypoint is now the waypoint that is in front of us
|
||||
if (player->exiting)
|
||||
{
|
||||
player->nextwaypoint = K_GetFinishLineWaypoint();
|
||||
// Player has finished, we don't need to calculate this
|
||||
player->distancetofinish = 0U;
|
||||
}
|
||||
else
|
||||
else if ((player->nextwaypoint != NULL) && (finishline != NULL))
|
||||
{
|
||||
waypoint_t *finishline = K_GetFinishLineWaypoint();
|
||||
waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player);
|
||||
const boolean useshortcuts = false;
|
||||
const boolean huntbackwards = false;
|
||||
boolean pathfindsuccess = false;
|
||||
path_t pathtofinish = {};
|
||||
|
||||
if (nextwaypoint != NULL)
|
||||
pathfindsuccess =
|
||||
K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards);
|
||||
|
||||
// Update the player's distance to the finish line if a path was found.
|
||||
// Using shortcuts won't find a path, so distance won't be updated until the player gets back on track
|
||||
if (pathfindsuccess == true)
|
||||
{
|
||||
// If nextwaypoint is NULL, it means we don't want to update the waypoint until we touch another one.
|
||||
// player->nextwaypoint will keep its previous value in this case.
|
||||
player->nextwaypoint = nextwaypoint;
|
||||
}
|
||||
// Add euclidean distance to the next waypoint to the distancetofinish
|
||||
UINT32 adddist;
|
||||
fixed_t disttowaypoint =
|
||||
P_AproxDistance(
|
||||
(player->mo->x >> FRACBITS) - (player->nextwaypoint->mobj->x >> FRACBITS),
|
||||
(player->mo->y >> FRACBITS) - (player->nextwaypoint->mobj->y >> FRACBITS));
|
||||
disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS));
|
||||
|
||||
// nextwaypoint is now the waypoint that is in front of us
|
||||
if ((player->nextwaypoint != NULL) && (finishline != NULL))
|
||||
{
|
||||
const boolean useshortcuts = false;
|
||||
const boolean huntbackwards = false;
|
||||
boolean pathfindsuccess = false;
|
||||
path_t pathtofinish = {};
|
||||
adddist = (UINT32)disttowaypoint;
|
||||
|
||||
pathfindsuccess =
|
||||
K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards);
|
||||
player->distancetofinish = pathtofinish.totaldist + adddist;
|
||||
Z_Free(pathtofinish.array);
|
||||
|
||||
// Update the player's distance to the finish line if a path was found.
|
||||
// Using shortcuts won't find a path, so distance won't be updated until the player gets back on track
|
||||
if (pathfindsuccess == true)
|
||||
// distancetofinish is currently a flat distance to the finish line, but in order to be fully
|
||||
// correct we need to add to it the length of the entire circuit multiplied by the number of laps
|
||||
// left after this one. This will give us the total distance to the finish line, and allow item
|
||||
// distance calculation to work easily
|
||||
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U)
|
||||
{
|
||||
// Add euclidean distance to the next waypoint to the distancetofinish
|
||||
UINT32 adddist;
|
||||
fixed_t disttowaypoint =
|
||||
P_AproxDistance(
|
||||
(player->mo->x >> FRACBITS) - (player->nextwaypoint->mobj->x >> FRACBITS),
|
||||
(player->mo->y >> FRACBITS) - (player->nextwaypoint->mobj->y >> FRACBITS));
|
||||
disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS));
|
||||
const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps);
|
||||
|
||||
adddist = (UINT32)disttowaypoint;
|
||||
player->distancetofinish += numfulllapsleft * K_GetCircuitLength();
|
||||
|
||||
player->distancetofinish = pathtofinish.totaldist + adddist;
|
||||
Z_Free(pathtofinish.array);
|
||||
|
||||
// distancetofinish is currently a flat distance to the finish line, but in order to be fully
|
||||
// correct we need to add to it the length of the entire circuit multiplied by the number of laps
|
||||
// left after this one. This will give us the total distance to the finish line, and allow item
|
||||
// distance calculation to work easily
|
||||
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U)
|
||||
// An additional HACK, to fix looking backwards towards the finish line
|
||||
// If the player's next waypoint is the finishline and the angle distance from player to
|
||||
// connectin waypoints implies they're closer to a next waypoint, add a full track distance
|
||||
if (player->nextwaypoint == finishline)
|
||||
{
|
||||
const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps);
|
||||
|
||||
player->distancetofinish += numfulllapsleft * K_GetCircuitLength();
|
||||
|
||||
// An additional HACK, to fix looking backwards towards the finish line
|
||||
// If the player's next waypoint is the finishline and the angle distance from player to
|
||||
// connectin waypoints implies they're closer to a next waypoint, add a full track distance
|
||||
if (player->nextwaypoint == finishline)
|
||||
if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true)
|
||||
{
|
||||
if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true)
|
||||
{
|
||||
player->distancetofinish += K_GetCircuitLength();
|
||||
}
|
||||
player->distancetofinish += K_GetCircuitLength();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6353,6 +6446,11 @@ void K_UpdateDistanceFromFinishLine(player_t *const player)
|
|||
}
|
||||
}
|
||||
|
||||
INT32 K_GetKartRingPower(player_t *player)
|
||||
{
|
||||
return (((9 - player->kartspeed) + (9 - player->kartweight)) / 2);
|
||||
}
|
||||
|
||||
// Returns false if this player being placed here causes them to collide with any other player
|
||||
// Used in g_game.c for match etc. respawning
|
||||
// This does not check along the z because the z is not correctly set for the spawnee at this point
|
||||
|
|
@ -6421,6 +6519,15 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
|
|||
return turnvalue;
|
||||
}
|
||||
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
turnvalue = 5*turnvalue/4; // Base increase to turning
|
||||
turnvalue = FixedMul(
|
||||
turnvalue * FRACUNIT,
|
||||
K_BotRubberband(player)
|
||||
) / FRACUNIT;
|
||||
}
|
||||
|
||||
if (player->kartstuff[k_drift] != 0 && P_IsObjectOnGround(player->mo))
|
||||
{
|
||||
fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, KART_FULLTURN*FRACUNIT);
|
||||
|
|
@ -6618,10 +6725,7 @@ static void K_KartDrift(player_t *player, boolean onground)
|
|||
|
||||
// Disable drift-sparks until you're going fast enough
|
||||
if (player->kartstuff[k_getsparks] == 0
|
||||
|| (player->kartstuff[k_offroad]
|
||||
&& !player->kartstuff[k_invincibilitytimer]
|
||||
&& !player->kartstuff[k_hyudorotimer]
|
||||
&& !EITHERSNEAKER(player)))
|
||||
|| (player->kartstuff[k_offroad] && K_ApplyOffroad(player)))
|
||||
driftadditive = 0;
|
||||
|
||||
// Inbetween minspeed and minspeed*2, it'll keep your previous drift-spark state.
|
||||
|
|
@ -6895,7 +6999,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
else if (cmd->buttons & BT_ATTACK)
|
||||
player->pflags |= PF_ATTACKDOWN;
|
||||
|
||||
if (player && player->mo && player->mo->health > 0 && !player->spectator && !(player->exiting || mapreset) && leveltime > starttime
|
||||
if (player && player->mo && player->mo->health > 0 && !player->spectator && !mapreset && leveltime > starttime
|
||||
&& player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_squishedtimer] == 0 && player->kartstuff[k_respawn] == 0)
|
||||
{
|
||||
// First, the really specific, finicky items that function without the item being directly in your item slot.
|
||||
|
|
@ -9315,15 +9419,23 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
|
|||
if (players[tab[i].num].spectator || !players[tab[i].num].mo)
|
||||
continue; //ignore them.
|
||||
|
||||
if (netgame // don't draw it offline
|
||||
&& ( tab[i].num != serverplayer || ! server_lagless ))
|
||||
HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0);
|
||||
if (netgame) // don't draw ping offline
|
||||
{
|
||||
if (players[tab[i].num].bot)
|
||||
{
|
||||
; // TODO: Put a graphic here to indicate this player is a bot!
|
||||
}
|
||||
else if (tab[i].num != serverplayer || !server_lagless)
|
||||
{
|
||||
HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0);
|
||||
}
|
||||
}
|
||||
|
||||
STRBUFCPY(strtime, tab[i].name);
|
||||
|
||||
y2 = y;
|
||||
|
||||
if (playerconsole[tab[i].num] == 0 && server_lagless)
|
||||
if (netgame && playerconsole[tab[i].num] == 0 && server_lagless && !players[tab[i].num].bot)
|
||||
{
|
||||
y2 = ( y - 4 );
|
||||
|
||||
|
|
@ -10093,7 +10205,7 @@ static void K_drawKartMinimap(void)
|
|||
g = g->next;
|
||||
}
|
||||
|
||||
if (!stplyr->mo || stplyr->spectator) // do we need the latter..?
|
||||
if (!stplyr->mo || stplyr->spectator || stplyr->exiting)
|
||||
return;
|
||||
|
||||
localplayers[numlocalplayers] = stplyr-players;
|
||||
|
|
@ -10105,7 +10217,7 @@ static void K_drawKartMinimap(void)
|
|||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
if (!players[i].mo || players[i].spectator)
|
||||
if (!players[i].mo || players[i].spectator || players[i].exiting)
|
||||
continue;
|
||||
|
||||
if (i != displayplayers[0] || r_splitscreen)
|
||||
|
|
@ -10815,7 +10927,7 @@ static void K_drawDistributionDebugger(void)
|
|||
|
||||
for (i = 1; i < NUMKARTRESULTS; i++)
|
||||
{
|
||||
const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush);
|
||||
const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot);
|
||||
if (itemodds <= 0)
|
||||
continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ fixed_t K_GetKartGameSpeedScalar(SINT8 value);
|
|||
extern consvar_t *KartItemCVars[NUMKARTRESULTS-1];
|
||||
|
||||
INT32 K_GetShieldFromItem(INT32 item);
|
||||
fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against);
|
||||
void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid);
|
||||
void K_KartPainEnergyFling(player_t *player);
|
||||
void K_FlipFromObject(mobj_t *mo, mobj_t *master);
|
||||
|
|
@ -53,6 +54,7 @@ void K_UpdateHnextList(player_t *player, boolean clean);
|
|||
void K_DropHnextList(player_t *player, boolean keepshields);
|
||||
void K_RepairOrbitChain(mobj_t *orbit);
|
||||
player_t *K_FindJawzTarget(mobj_t *actor, player_t *source);
|
||||
INT32 K_GetKartRingPower(player_t *player);
|
||||
void K_UpdateDistanceFromFinishLine(player_t *const player);
|
||||
boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y);
|
||||
INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue);
|
||||
|
|
@ -62,6 +64,7 @@ void K_DropItems(player_t *player);
|
|||
void K_StripItems(player_t *player);
|
||||
void K_StripOther(player_t *player);
|
||||
void K_MomentumToFacing(player_t *player);
|
||||
boolean K_ApplyOffroad(player_t *player);
|
||||
fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed);
|
||||
fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower);
|
||||
fixed_t K_GetKartAccel(player_t *player);
|
||||
|
|
|
|||
|
|
@ -305,9 +305,10 @@ void K_PlayerForfeit(UINT8 playernum, boolean pointloss)
|
|||
if (i == playernum)
|
||||
continue;
|
||||
|
||||
theirpower = PWRLVRECORD_DEF;
|
||||
if (clientpowerlevels[i][powertype] != 0) // No power level acts as 5000 (used for splitscreen guests)
|
||||
theirpower = clientpowerlevels[i][powertype];
|
||||
if (clientpowerlevels[i][powertype] == 0) // No power level (splitscreen guests, bots)
|
||||
continue;
|
||||
|
||||
theirpower = clientpowerlevels[i][powertype];
|
||||
|
||||
diff = yourpower - theirpower;
|
||||
inc -= K_CalculatePowerLevelInc(diff);
|
||||
|
|
|
|||
|
|
@ -271,9 +271,12 @@ waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj)
|
|||
waypoint_t *checkwaypoint = NULL;
|
||||
fixed_t closestdist = INT32_MAX;
|
||||
fixed_t checkdist = INT32_MAX;
|
||||
fixed_t bestfindist = INT32_MAX;
|
||||
|
||||
for (i = 0; i < numwaypoints; i++)
|
||||
{
|
||||
fixed_t rad;
|
||||
|
||||
checkwaypoint = &waypointheap[i];
|
||||
|
||||
checkdist = P_AproxDistance(
|
||||
|
|
@ -281,7 +284,34 @@ waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj)
|
|||
(mobj->y / FRACUNIT) - (checkwaypoint->mobj->y / FRACUNIT));
|
||||
checkdist = P_AproxDistance(checkdist, ((mobj->z / FRACUNIT) - (checkwaypoint->mobj->z / FRACUNIT)) * 4);
|
||||
|
||||
if (checkdist < closestdist)
|
||||
rad = (checkwaypoint->mobj->radius / FRACUNIT);
|
||||
|
||||
if (closestdist < rad && checkdist < rad && finishline != NULL)
|
||||
{
|
||||
const boolean useshortcuts = false;
|
||||
const boolean huntbackwards = false;
|
||||
boolean pathfindsuccess = false;
|
||||
path_t pathtofinish = {};
|
||||
|
||||
// If the mobj is touching multiple waypoints at once,
|
||||
// then solve ties by taking the one closest to the finish line.
|
||||
// Prevents position from flickering wildly when taking turns.
|
||||
|
||||
pathfindsuccess =
|
||||
K_PathfindToWaypoint(checkwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards);
|
||||
|
||||
if (pathfindsuccess == true)
|
||||
{
|
||||
if ((INT32)(pathtofinish.totaldist) < bestfindist)
|
||||
{
|
||||
bestwaypoint = checkwaypoint;
|
||||
bestfindist = pathtofinish.totaldist;
|
||||
}
|
||||
|
||||
Z_Free(pathtofinish.array);
|
||||
}
|
||||
}
|
||||
else if (checkdist < closestdist && bestfindist == INT32_MAX)
|
||||
{
|
||||
if (!P_CheckSight(mobj, checkwaypoint->mobj))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
#include "p_mobj.h"
|
||||
#include "g_game.h"
|
||||
#include "r_things.h"
|
||||
#include "b_bot.h"
|
||||
#include "k_bot.h"
|
||||
#include "z_zone.h"
|
||||
|
||||
#include "lua_script.h"
|
||||
|
|
@ -968,6 +968,12 @@ boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd)
|
|||
// Hook for B_BuildTailsTiccmd by skin name
|
||||
boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
||||
{
|
||||
#if 1
|
||||
(void)sonic;
|
||||
(void)tails;
|
||||
(void)cmd;
|
||||
return false;
|
||||
#else
|
||||
hook_p hookp;
|
||||
boolean hooked = false;
|
||||
if (!gL || !(hooksAvailable[hook_BotAI/8] & (1<<(hook_BotAI%8))))
|
||||
|
|
@ -1024,6 +1030,7 @@ boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
|
||||
lua_settop(gL, 0);
|
||||
return hooked;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Hook for linedef executors
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ static int player_get(lua_State *L)
|
|||
else if (fastcmp(field,"spectator"))
|
||||
lua_pushboolean(L, plr->spectator);
|
||||
else if (fastcmp(field,"bot"))
|
||||
lua_pushinteger(L, plr->bot);
|
||||
lua_pushboolean(L, plr->bot);
|
||||
else if (fastcmp(field,"jointime"))
|
||||
lua_pushinteger(L, plr->jointime);
|
||||
else if (fastcmp(field,"splitscreenindex"))
|
||||
|
|
|
|||
|
|
@ -3570,7 +3570,6 @@ void A_AttractChase(mobj_t *actor)
|
|||
|
||||
if (actor->extravalue1) // SRB2Kart
|
||||
{
|
||||
#define RINGBOOSTPWR (((9 - actor->target->player->kartspeed) + (9 - actor->target->player->kartweight)) / 2)
|
||||
if (!actor->target || P_MobjWasRemoved(actor->target) || !actor->target->player)
|
||||
{
|
||||
P_RemoveMobj(actor);
|
||||
|
|
@ -3588,7 +3587,7 @@ void A_AttractChase(mobj_t *actor)
|
|||
angle_t offset = FixedAngle(18<<FRACBITS);
|
||||
|
||||
// Base add is 3 tics for 9,9, adds 1 tic for each point closer to the 1,1 end
|
||||
actor->target->player->kartstuff[k_ringboost] += RINGBOOSTPWR+3;
|
||||
actor->target->player->kartstuff[k_ringboost] += K_GetKartRingPower(actor->target->player)+3;
|
||||
S_StartSound(actor->target, sfx_s1b5);
|
||||
|
||||
sparkle = P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, MT_RINGSPARKS);
|
||||
|
|
@ -3616,7 +3615,7 @@ void A_AttractChase(mobj_t *actor)
|
|||
if (actor->extravalue1 >= 16)
|
||||
{
|
||||
if (actor->target->player->kartstuff[k_rings] >= 20)
|
||||
actor->target->player->kartstuff[k_ringboost] += RINGBOOSTPWR+3;
|
||||
actor->target->player->kartstuff[k_ringboost] += K_GetKartRingPower(actor->target->player)+3;
|
||||
else
|
||||
P_GivePlayerRings(actor->target->player, 1);
|
||||
|
||||
|
|
@ -3645,7 +3644,6 @@ void A_AttractChase(mobj_t *actor)
|
|||
actor->extravalue1++;
|
||||
}
|
||||
}
|
||||
#undef RINGBOOSTPWR
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -161,8 +161,6 @@ void P_DoNightsScore(player_t *player)
|
|||
return; // Don't do any fancy shit for failures.
|
||||
|
||||
dummymo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+player->mo->height/2, MT_NIGHTSCORE);
|
||||
if (player->bot)
|
||||
player = &players[consoleplayer];
|
||||
|
||||
if (G_IsSpecialStage(gamemap)) // Global link count? Maybe not a good idea...
|
||||
{
|
||||
|
|
@ -758,8 +756,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
// ***************************** //
|
||||
// Special Stage Token
|
||||
case MT_EMMY:
|
||||
if (player->bot)
|
||||
return;
|
||||
tokenlist += special->health;
|
||||
|
||||
if (ALL7EMERALDS(emeralds)) // Got all 7
|
||||
|
|
@ -776,9 +772,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
|
||||
// Emerald Hunt
|
||||
case MT_EMERHUNT:
|
||||
if (player->bot)
|
||||
return;
|
||||
|
||||
if (hunt1 == special)
|
||||
hunt1 = NULL;
|
||||
else if (hunt2 == special)
|
||||
|
|
@ -807,9 +800,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
case MT_EMERALD5:
|
||||
case MT_EMERALD6:
|
||||
case MT_EMERALD7:
|
||||
if (player->bot)
|
||||
return;
|
||||
|
||||
if (special->threshold)
|
||||
player->powers[pw_emeralds] |= special->info->speed;
|
||||
else
|
||||
|
|
@ -837,12 +827,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
// Secret emblem thingy
|
||||
case MT_EMBLEM:
|
||||
{
|
||||
if (demo.playback || player->bot)
|
||||
if (demo.playback)
|
||||
return;
|
||||
|
||||
emblemlocations[special->health-1].collected = true;
|
||||
|
||||
M_UpdateUnlockablesAndExtraEmblems(false);
|
||||
|
||||
G_SaveGameData(false);
|
||||
break;
|
||||
}
|
||||
|
|
@ -850,8 +839,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
// CTF Flags
|
||||
case MT_REDFLAG:
|
||||
case MT_BLUEFLAG:
|
||||
if (player->bot)
|
||||
return;
|
||||
if (player->powers[pw_flashing] || player->tossdelay)
|
||||
return;
|
||||
if (!special->spawnpoint)
|
||||
|
|
@ -922,8 +909,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
// NiGHTS gameplay items and powerups //
|
||||
// ********************************** //
|
||||
/*case MT_NIGHTSDRONE:
|
||||
if (player->bot)
|
||||
return;
|
||||
if (player->exiting)
|
||||
return;
|
||||
if (player->bonustime)
|
||||
|
|
@ -1077,9 +1062,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
return;
|
||||
case MT_EGGCAPSULE:
|
||||
if (player->bot)
|
||||
return;
|
||||
|
||||
// make sure everything is as it should be, THEN take rings from players in special stages
|
||||
if (player->pflags & PF_NIGHTSMODE && !toucher->target)
|
||||
return;
|
||||
|
|
@ -1181,7 +1163,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
return;
|
||||
/*case MT_NIGHTSSUPERLOOP:
|
||||
if (player->bot || !(player->pflags & PF_NIGHTSMODE))
|
||||
if (!(player->pflags & PF_NIGHTSMODE))
|
||||
return;
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
player->powers[pw_nights_superloop] = (UINT16)special->info->speed;
|
||||
|
|
@ -1203,7 +1185,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
break;
|
||||
case MT_NIGHTSDRILLREFILL:
|
||||
if (player->bot || !(player->pflags & PF_NIGHTSMODE))
|
||||
if (!(player->pflags & PF_NIGHTSMODE))
|
||||
return;
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
player->drillmeter = special->info->speed;
|
||||
|
|
@ -1225,7 +1207,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
break;
|
||||
case MT_NIGHTSHELPER:
|
||||
if (player->bot || !(player->pflags & PF_NIGHTSMODE))
|
||||
if (!(player->pflags & PF_NIGHTSMODE))
|
||||
return;
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
{
|
||||
|
|
@ -1257,7 +1239,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
break;
|
||||
case MT_NIGHTSEXTRATIME:
|
||||
if (player->bot || !(player->pflags & PF_NIGHTSMODE))
|
||||
if (!(player->pflags & PF_NIGHTSMODE))
|
||||
return;
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
{
|
||||
|
|
@ -1287,7 +1269,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
break;
|
||||
case MT_NIGHTSLINKFREEZE:
|
||||
if (player->bot || !(player->pflags & PF_NIGHTSMODE))
|
||||
if (!(player->pflags & PF_NIGHTSMODE))
|
||||
return;
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
{
|
||||
|
|
@ -1358,8 +1340,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
if (playeringame[i] && players[i].pflags & PF_NIGHTSMODE)
|
||||
players[i].drillmeter += TICRATE/2;
|
||||
}
|
||||
else if (player->bot)
|
||||
players[consoleplayer].drillmeter += TICRATE/2;
|
||||
else
|
||||
player->drillmeter += TICRATE/2;
|
||||
|
||||
|
|
@ -1393,9 +1373,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
thinker_t *th;
|
||||
mobj_t *mo2;
|
||||
|
||||
if (player->bot)
|
||||
return;
|
||||
|
||||
junk.tag = 649;
|
||||
EV_DoElevator(&junk, bridgeFall, false);
|
||||
|
||||
|
|
@ -1415,8 +1392,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
break;
|
||||
case MT_FIREFLOWER:
|
||||
if (player->bot)
|
||||
return;
|
||||
player->powers[pw_shield] |= SH_FIREFLOWER;
|
||||
toucher->color = SKINCOLOR_WHITE;
|
||||
G_GhostAddColor(player - players, GHC_FIREFLOWER);
|
||||
|
|
@ -1426,9 +1401,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
// Misc touchables //
|
||||
// *************** //
|
||||
case MT_STARPOST:
|
||||
if (player->bot)
|
||||
return;
|
||||
//
|
||||
// SRB2kart: make sure the player will have enough checkpoints to touch
|
||||
if (circuitmap && special->health - player->starpostnum > 1)
|
||||
{
|
||||
|
|
@ -1631,7 +1603,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
if (player->powers[pw_invulnerability] || player->powers[pw_flashing]
|
||||
|| (player->powers[pw_super] && !(ALL7EMERALDS(player->powers[pw_emeralds]))))
|
||||
return;
|
||||
if (player->powers[pw_shield] || player->bot) //If One-Hit Shield
|
||||
if (player->powers[pw_shield]) //If One-Hit Shield
|
||||
{
|
||||
P_RemoveShield(player);
|
||||
S_StartSound(toucher, sfx_shldls); // Ba-Dum! Shield loss.
|
||||
|
|
@ -1716,8 +1688,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
|
||||
default: // SOC or script pickup
|
||||
if (player->bot)
|
||||
return;
|
||||
P_SetTarget(&special->target, toucher);
|
||||
break;
|
||||
}
|
||||
|
|
@ -2008,7 +1978,7 @@ boolean P_CheckRacers(void)
|
|||
// Check if all the players in the race have finished. If so, end the level.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator || players[i].exiting || !players[i].lives)
|
||||
if (!playeringame[i] || players[i].spectator || players[i].exiting || players[i].bot || !players[i].lives)
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
|
@ -2280,8 +2250,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
|
|||
target->flags |= MF_NOBLOCKMAP|MF_NOCLIPHEIGHT;
|
||||
P_SetThingPosition(target);
|
||||
|
||||
if (!target->player->bot && !G_IsSpecialStage(gamemap)
|
||||
&& G_GametypeUsesLives())
|
||||
if (!target->player->bot && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives())
|
||||
{
|
||||
target->player->lives -= 1; // Lose a life Tails 03-11-2000
|
||||
|
||||
|
|
@ -2294,6 +2263,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
target->player->playerstate = PST_DEAD;
|
||||
|
||||
if (target->player == &players[consoleplayer])
|
||||
|
|
@ -3038,7 +3008,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
return false;
|
||||
|
||||
// Make sure that boxes cannot be popped by enemies, red rings, etc.
|
||||
if (target->flags & MF_MONITOR && ((!source || !source->player || source->player->bot) || (inflictor && !inflictor->player)))
|
||||
if (target->flags & MF_MONITOR && ((!source || !source->player) || (inflictor && !inflictor->player)))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
85
src/p_map.c
85
src/p_map.c
|
|
@ -491,89 +491,6 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object)
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void P_DoTailsCarry(player_t *sonic, player_t *tails)
|
||||
{
|
||||
INT32 p;
|
||||
fixed_t zdist; // z distance between the two players' bottoms
|
||||
|
||||
if ((tails->pflags & PF_CARRIED) && tails->mo->tracer == sonic->mo)
|
||||
return;
|
||||
if ((sonic->pflags & PF_CARRIED) && sonic->mo->tracer == tails->mo)
|
||||
return;
|
||||
|
||||
//if (!tails->powers[pw_tailsfly] && !(tails->charability == CA_FLY && (tails->mo->state >= &states[S_PLAY_SPC1] && tails->mo->state <= &states[S_PLAY_SPC4])))
|
||||
// return; // SRB2kart - no changey statey
|
||||
|
||||
if (tails->bot == 1)
|
||||
return;
|
||||
|
||||
if (sonic->pflags & PF_NIGHTSMODE)
|
||||
return;
|
||||
|
||||
if (sonic->mo->tracer && sonic->mo->tracer->type == MT_TUBEWAYPOINT
|
||||
&& !(sonic->pflags & PF_ROPEHANG))
|
||||
return; // don't steal players from zoomtubes!
|
||||
|
||||
if ((sonic->mo->eflags & MFE_VERTICALFLIP) != (tails->mo->eflags & MFE_VERTICALFLIP))
|
||||
return; // Both should be in same gravity
|
||||
|
||||
if (tails->mo->eflags & MFE_VERTICALFLIP)
|
||||
{
|
||||
if (tails->mo->ceilingz - (tails->mo->z + tails->mo->height) < sonic->mo->height-FixedMul(2*FRACUNIT, sonic->mo->scale))
|
||||
return;
|
||||
}
|
||||
else if (tails->mo->z - tails->mo->floorz < sonic->mo->height-FixedMul(2*FRACUNIT, sonic->mo->scale))
|
||||
return; // No room to pick up this guy!
|
||||
|
||||
// Search in case another player is already being carried by this fox.
|
||||
for (p = 0; p < MAXPLAYERS; p++)
|
||||
if (playeringame[p] && players[p].mo
|
||||
&& players[p].pflags & PF_CARRIED && players[p].mo->tracer == tails->mo)
|
||||
return;
|
||||
|
||||
if (tails->mo->eflags & MFE_VERTICALFLIP)
|
||||
zdist = (sonic->mo->z + sonic->mo->height) - (tails->mo->z + tails->mo->height);
|
||||
else
|
||||
zdist = tails->mo->z - sonic->mo->z;
|
||||
|
||||
if (zdist <= sonic->mo->height + FixedMul(FRACUNIT, sonic->mo->scale)
|
||||
&& zdist > sonic->mo->height*2/3
|
||||
&& P_MobjFlip(tails->mo)*sonic->mo->momz <= 0)
|
||||
{
|
||||
// Why block opposing teams from tailsflying each other?
|
||||
// Sneaking into the hands of a flying tails player in Race might be a viable strategy, who knows.
|
||||
/*
|
||||
if (gametype == GT_RACE || gametype == GT_COMPETITION
|
||||
|| (netgame && (tails->spectator || sonic->spectator))
|
||||
|| (G_TagGametype() && (!(tails->pflags & PF_TAGIT) != !(sonic->pflags & PF_TAGIT)))
|
||||
|| (gametype == GT_MATCH)
|
||||
|| (G_GametypeHasTeams() && tails->ctfteam != sonic->ctfteam))
|
||||
sonic->pflags &= ~PF_CARRIED; */
|
||||
if (tails->spectator || sonic->spectator || G_RaceGametype()) // SRB2kart
|
||||
sonic->pflags &= ~PF_CARRIED;
|
||||
else
|
||||
{
|
||||
if (sonic-players == consoleplayer && botingame)
|
||||
//CV_SetValue(&cv_analog2, false);
|
||||
P_ResetPlayer(sonic);
|
||||
P_SetTarget(&sonic->mo->tracer, tails->mo);
|
||||
sonic->pflags |= PF_CARRIED;
|
||||
S_StartSound(sonic->mo, sfx_s3k4a);
|
||||
P_UnsetThingPosition(sonic->mo);
|
||||
sonic->mo->x = tails->mo->x;
|
||||
sonic->mo->y = tails->mo->y;
|
||||
P_SetThingPosition(sonic->mo);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sonic-players == consoleplayer && botingame)
|
||||
//CV_SetValue(&cv_analog2, true);
|
||||
sonic->pflags &= ~PF_CARRIED;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// PIT_CheckThing
|
||||
//
|
||||
|
|
@ -1463,8 +1380,6 @@ static boolean PIT_CheckThing(mobj_t *thing)
|
|||
}
|
||||
}
|
||||
else if (thing->player) {
|
||||
if (thing->player-players == consoleplayer && botingame)
|
||||
//CV_SetValue(&cv_analog2, true);
|
||||
thing->player->pflags &= ~PF_CARRIED;
|
||||
}*/
|
||||
|
||||
|
|
|
|||
53
src/p_mobj.c
53
src/p_mobj.c
|
|
@ -30,7 +30,7 @@
|
|||
#include "info.h"
|
||||
#include "i_video.h"
|
||||
#include "lua_hook.h"
|
||||
#include "b_bot.h"
|
||||
#include "k_bot.h"
|
||||
#ifdef ESLOPE
|
||||
#include "p_slopes.h"
|
||||
#endif
|
||||
|
|
@ -1691,10 +1691,6 @@ void P_XYMovement(mobj_t *mo)
|
|||
// blocked move
|
||||
moved = false;
|
||||
|
||||
if (player) {
|
||||
if (player->bot)
|
||||
B_MoveBlocked(player);
|
||||
}
|
||||
//{ SRB2kart - Jawz
|
||||
if (mo->type == MT_JAWZ || mo->type == MT_JAWZ_DUD)
|
||||
{
|
||||
|
|
@ -11793,9 +11789,20 @@ void P_SpawnPlayer(INT32 playernum)
|
|||
}
|
||||
|
||||
// spawn as spectator determination
|
||||
if (multiplayer && demo.playback); // Don't mess with spectator values since the demo setup handles them already.
|
||||
if (multiplayer && demo.playback)
|
||||
{
|
||||
; // Don't mess with spectator values since the demo setup handles them already.
|
||||
}
|
||||
else if (!G_GametypeHasSpectators())
|
||||
{
|
||||
// We don't have spectators
|
||||
p->spectator = false;
|
||||
}
|
||||
else if (p->bot)
|
||||
{
|
||||
// No point in a spectating bot!
|
||||
p->spectator = false;
|
||||
}
|
||||
else if (netgame && p->jointime <= 1 && pcount)
|
||||
{
|
||||
p->spectator = true;
|
||||
|
|
@ -12755,12 +12762,44 @@ ML_NOCLIMB : Direction not controllable
|
|||
mobj->extravalue2 = 0;
|
||||
}
|
||||
|
||||
|
||||
// Sryder 2018-12-7: Grabbed this from the old MT_BOSS3WAYPOINT section so they'll be in the waypointcap instead
|
||||
P_SetTarget(&mobj->tracer, waypointcap);
|
||||
P_SetTarget(&waypointcap, mobj);
|
||||
break;
|
||||
}
|
||||
case MT_BOTHINT:
|
||||
{
|
||||
// Change size
|
||||
if (mthing->angle > 0)
|
||||
{
|
||||
mobj->radius = mthing->angle * FRACUNIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
mobj->radius = 32 * mapobjectscale;
|
||||
}
|
||||
|
||||
// Steer away instead of towards
|
||||
if (mthing->options & MTF_AMBUSH)
|
||||
{
|
||||
mobj->extravalue1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mobj->extravalue1 = 1;
|
||||
}
|
||||
|
||||
// Steering amount
|
||||
if (mthing->extrainfo == 0)
|
||||
{
|
||||
mobj->extravalue2 = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
mobj->extravalue2 = mthing->extrainfo;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// SRB2Kart
|
||||
case MT_BALLOON:
|
||||
mobj->color = (1 + (mthing->angle % (MAXSKINCOLORS-1)));
|
||||
|
|
|
|||
|
|
@ -79,12 +79,6 @@ static inline void P_ArchivePlayer(void)
|
|||
WRITEUINT32(save_p, player->score);
|
||||
WRITEINT32(save_p, pllives);
|
||||
WRITEINT32(save_p, player->continues);
|
||||
|
||||
if (botskin)
|
||||
{
|
||||
WRITEUINT8(save_p, botskin);
|
||||
WRITEUINT8(save_p, botcolor);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -98,16 +92,6 @@ static inline void P_UnArchivePlayer(void)
|
|||
savedata.score = READINT32(save_p);
|
||||
savedata.lives = READINT32(save_p);
|
||||
savedata.continues = READINT32(save_p);
|
||||
|
||||
if (savedata.botcolor)
|
||||
{
|
||||
savedata.botskin = READUINT8(save_p);
|
||||
if (savedata.botskin-1 >= numskins)
|
||||
savedata.botskin = 0;
|
||||
savedata.botcolor = READUINT8(save_p);
|
||||
}
|
||||
else
|
||||
savedata.botskin = 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -284,6 +268,11 @@ static void P_NetArchivePlayers(void)
|
|||
|
||||
WRITEUINT32(save_p, players[i].distancetofinish);
|
||||
WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].nextwaypoint));
|
||||
|
||||
WRITEUINT8(save_p, players[i].botvars.difficulty);
|
||||
WRITEUINT32(save_p, players[i].botvars.itemdelay);
|
||||
WRITEUINT32(save_p, players[i].botvars.itemconfirm);
|
||||
WRITESINT8(save_p, players[i].botvars.turnconfirm);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -455,6 +444,11 @@ static void P_NetUnArchivePlayers(void)
|
|||
|
||||
players[i].distancetofinish = READUINT32(save_p);
|
||||
players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save_p);
|
||||
|
||||
players[i].botvars.difficulty = READUINT8(save_p);
|
||||
players[i].botvars.itemdelay = READUINT32(save_p);
|
||||
players[i].botvars.itemconfirm = READUINT32(save_p);
|
||||
players[i].botvars.turnconfirm = READSINT8(save_p);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3247,7 +3241,7 @@ static inline void P_ArchiveMisc(void)
|
|||
|
||||
lastmapsaved = gamemap;
|
||||
|
||||
WRITEUINT16(save_p, (botskin ? (emeralds|(1<<10)) : emeralds)+357);
|
||||
WRITEUINT16(save_p, emeralds+357);
|
||||
WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@
|
|||
#include "k_battle.h" // K_SpawnBattleCapsules
|
||||
#include "k_pwrlv.h"
|
||||
#include "k_waypoint.h"
|
||||
#include "k_bot.h"
|
||||
|
||||
//
|
||||
// Map MD5, calculated on level load.
|
||||
|
|
@ -2489,6 +2490,11 @@ static void P_LevelInitStuff(void)
|
|||
|
||||
memset(&battleovertime, 0, sizeof(struct battleovertime));
|
||||
speedscramble = encorescramble = -1;
|
||||
|
||||
if (!modeattacking)
|
||||
{
|
||||
K_UpdateMatchRaceBots();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -2888,7 +2894,7 @@ boolean P_SetupLevel(boolean skipprecip)
|
|||
P_Initsecnode();
|
||||
|
||||
if (netgame || multiplayer)
|
||||
cv_debug = botskin = 0;
|
||||
cv_debug = 0;
|
||||
|
||||
if (metalplayback)
|
||||
G_StopMetalDemo();
|
||||
|
|
@ -3339,7 +3345,7 @@ boolean P_SetupLevel(boolean skipprecip)
|
|||
/*if (cv_useranalog.value)
|
||||
CV_SetValue(&cv_analog, true);
|
||||
|
||||
if ((splitscreen && cv_useranalog2.value) || botingame)
|
||||
if (splitscreen && cv_useranalog2.value)
|
||||
CV_SetValue(&cv_analog2, true);
|
||||
|
||||
if (splitscreen > 1 && cv_useranalog3.value)
|
||||
|
|
@ -3410,9 +3416,6 @@ boolean P_SetupLevel(boolean skipprecip)
|
|||
players[consoleplayer].continues = savedata.continues;
|
||||
players[consoleplayer].lives = savedata.lives;
|
||||
players[consoleplayer].score = savedata.score;
|
||||
botskin = savedata.botskin;
|
||||
botcolor = savedata.botcolor;
|
||||
botingame = (savedata.botskin != 0);
|
||||
emeralds = savedata.emeralds;
|
||||
savedata.lives = 0;
|
||||
}
|
||||
|
|
|
|||
54
src/p_spec.c
54
src/p_spec.c
|
|
@ -2209,7 +2209,8 @@ static void K_HandleLapDecrement(player_t *player)
|
|||
void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing)
|
||||
{
|
||||
// only used for the players currently
|
||||
if (thing && thing->player)
|
||||
if (!(thing && thing->player && !thing->player->spectator && !(thing->player->pflags & PF_TIMEOVER)))
|
||||
return;
|
||||
{
|
||||
player_t *player = thing->player;
|
||||
switch (line->special)
|
||||
|
|
@ -2280,13 +2281,9 @@ static mobj_t *P_GetObjectTypeInSectorNum(mobjtype_t type, size_t s)
|
|||
static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
||||
{
|
||||
INT32 secnum = -1;
|
||||
mobj_t *bot = NULL;
|
||||
|
||||
I_Assert(!mo || !P_MobjWasRemoved(mo)); // If mo is there, mo must be valid!
|
||||
|
||||
if (mo && mo->player && botingame)
|
||||
bot = players[displayplayers[1]].mo;
|
||||
|
||||
// note: only commands with linedef types >= 400 && < 500 can be used
|
||||
switch (line->special)
|
||||
{
|
||||
|
|
@ -2425,9 +2422,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
{
|
||||
UINT8 i;
|
||||
|
||||
if (bot) // This might put poor Tails in a wall if he's too far behind! D: But okay, whatever! >:3
|
||||
P_TeleportMove(bot, bot->x + x, bot->y + y, bot->z + z);
|
||||
|
||||
for (i = 0; i <= r_splitscreen; i++)
|
||||
{
|
||||
if (mo->player == &players[displayplayers[i]] && camera[i].chase)
|
||||
|
|
@ -2450,8 +2444,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
if (!dest)
|
||||
return;
|
||||
|
||||
if (bot)
|
||||
P_Teleport(bot, dest->x, dest->y, dest->z, (line->flags & ML_NOCLIMB) ? mo->angle : dest->angle, (line->flags & ML_BLOCKPLAYERS) == 0, (line->flags & ML_EFFECT4) == ML_EFFECT4);
|
||||
if (line->flags & ML_BLOCKPLAYERS)
|
||||
P_Teleport(mo, dest->x, dest->y, dest->z, (line->flags & ML_NOCLIMB) ? mo->angle : dest->angle, false, (line->flags & ML_EFFECT4) == ML_EFFECT4);
|
||||
else
|
||||
|
|
@ -2884,19 +2876,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
mo->player->rmomx = mo->player->rmomy = 1;
|
||||
mo->player->cmomx = mo->player->cmomy = 0;
|
||||
P_ResetPlayer(mo->player);
|
||||
P_SetPlayerMobjState(mo, S_KART_STILL1); // SRB2kart - was S_PLAY_STND
|
||||
|
||||
// Reset bot too.
|
||||
if (bot) {
|
||||
if (line->flags & ML_NOCLIMB)
|
||||
P_TeleportMove(bot, mo->x, mo->y, mo->z);
|
||||
bot->momx = bot->momy = bot->momz = 1;
|
||||
bot->pmomz = 0;
|
||||
bot->player->rmomx = bot->player->rmomy = 1;
|
||||
bot->player->cmomx = bot->player->cmomy = 0;
|
||||
P_ResetPlayer(bot->player);
|
||||
P_SetPlayerMobjState(bot, S_KART_STILL1); // SRB2kart - was S_PLAY_STND
|
||||
}
|
||||
P_SetPlayerMobjState(mo, S_KART_STILL1);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -2928,13 +2908,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
mo->flags2 &= ~MF2_TWOD;
|
||||
else
|
||||
mo->flags2 |= MF2_TWOD;
|
||||
|
||||
// Copy effect to bot if necessary
|
||||
// (Teleport them to you so they don't break it.)
|
||||
if (bot && (bot->flags2 & MF2_TWOD) != (mo->flags2 & MF2_TWOD)) {
|
||||
bot->flags2 = (bot->flags2 & ~MF2_TWOD) | (mo->flags2 & MF2_TWOD);
|
||||
P_TeleportMove(bot, mo->x, mo->y, mo->z);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -2943,8 +2916,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
mo->flags2 &= ~MF2_OBJECTFLIP;
|
||||
else
|
||||
mo->flags2 |= MF2_OBJECTFLIP;
|
||||
if (bot)
|
||||
bot->flags2 = (bot->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP);
|
||||
break;
|
||||
|
||||
case 434: // Custom Power
|
||||
|
|
@ -2964,10 +2935,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
P_SetTarget(&dummy->target, mo);
|
||||
A_CustomPower(dummy);
|
||||
|
||||
if (bot) {
|
||||
P_SetTarget(&dummy->target, bot);
|
||||
A_CustomPower(dummy);
|
||||
}
|
||||
P_RemoveMobj(dummy);
|
||||
}
|
||||
break;
|
||||
|
|
@ -3036,8 +3003,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
if (line->flags & ML_NOCLIMB)
|
||||
fractime |= 1<<15; //more crazy &ing, as if music stuff wasn't enough
|
||||
mo->player->powers[pw_nocontrol] = fractime;
|
||||
if (bot)
|
||||
bot->player->powers[pw_nocontrol] = fractime;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -3047,8 +3012,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
|
|||
mo->destscale = FixedDiv(P_AproxDistance(line->dx, line->dy), 100<<FRACBITS);
|
||||
if (mo->destscale < FRACUNIT/100)
|
||||
mo->destscale = FRACUNIT/100;
|
||||
if (mo->player && bot)
|
||||
bot->destscale = mo->destscale;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -3879,7 +3842,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
|
|||
}
|
||||
break;
|
||||
case 11: // Special Stage Damage - Kind of like a mini-P_DamageMobj()
|
||||
if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super] || player->exiting || player->bot)
|
||||
if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super] || player->exiting)
|
||||
break;
|
||||
|
||||
if (!(player->powers[pw_shield] || player->mo->health > 1)) // Don't do anything if no shield or rings anyway
|
||||
|
|
@ -3923,7 +3886,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
|
|||
case 3: // Linedef executor requires all players present
|
||||
/// \todo check continues for proper splitscreen support?
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i] && !players[i].bot && players[i].mo && (gametype != GT_COOP || players[i].lives > 0))
|
||||
if (playeringame[i] && players[i].mo && (gametype != GT_COOP || players[i].lives > 0))
|
||||
{
|
||||
if (roversector)
|
||||
{
|
||||
|
|
@ -3981,8 +3944,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
|
|||
case 5: // Linedef executor
|
||||
case 6: // Linedef executor (7 Emeralds)
|
||||
case 7: // Linedef executor (NiGHTS Mare)
|
||||
if (!player->bot)
|
||||
P_LinedefExecute(sector->tag, player->mo, sector);
|
||||
P_LinedefExecute(sector->tag, player->mo, sector);
|
||||
break;
|
||||
case 8: // Tells pushable things to check FOFs
|
||||
break;
|
||||
|
|
@ -3992,7 +3954,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
|
|||
mobj_t *mo2;
|
||||
line_t junk;
|
||||
|
||||
if (player->bot || sector->ceilingdata || sector->floordata)
|
||||
if (sector->ceilingdata || sector->floordata)
|
||||
return;
|
||||
|
||||
// Find the center of the Eggtrap and release all the pretty animals!
|
||||
|
|
@ -4168,8 +4130,6 @@ DoneSection2:
|
|||
}
|
||||
|
||||
case 2: // Special stage GOAL sector / Exit Sector / CTF Flag Return
|
||||
if (player->bot)
|
||||
break;
|
||||
if (!useNightsSS && G_IsSpecialStage(gamemap) && sstimer > 6)
|
||||
sstimer = 6; // Just let P_Ticker take care of the rest.
|
||||
|
||||
|
|
|
|||
72
src/p_user.c
72
src/p_user.c
|
|
@ -40,7 +40,7 @@
|
|||
#include "st_stuff.h"
|
||||
#include "lua_script.h"
|
||||
#include "lua_hook.h"
|
||||
#include "b_bot.h"
|
||||
#include "k_bot.h"
|
||||
// Objectplace
|
||||
#include "m_cheat.h"
|
||||
// SRB2kart
|
||||
|
|
@ -706,10 +706,6 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
|
|||
{
|
||||
INT32 oldmare;
|
||||
|
||||
// Bots can't be super, silly!1 :P
|
||||
if (player->bot)
|
||||
return;
|
||||
|
||||
if (!(player->pflags & PF_NIGHTSMODE))
|
||||
{
|
||||
P_SetTarget(&player->mo->tracer, P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_NIGHTSCHAR));
|
||||
|
|
@ -935,8 +931,6 @@ void P_ResetPlayer(player_t *player)
|
|||
player->powers[pw_tailsfly] = 0;
|
||||
player->onconveyor = 0;
|
||||
player->skidtime = 0;
|
||||
/*if (player-players == consoleplayer && botingame)
|
||||
CV_SetValue(&cv_analog2, true);*/
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -1030,9 +1024,6 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
|
|||
if (!(G_BattleGametype()))
|
||||
return;
|
||||
|
||||
if (player->bot)
|
||||
player = &players[consoleplayer];
|
||||
|
||||
if (player->exiting) // srb2kart
|
||||
return;
|
||||
|
||||
|
|
@ -2285,7 +2276,7 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player)
|
|||
}
|
||||
|
||||
// Underwater audio cues
|
||||
if (P_IsLocalPlayer(player) && !player->bot)
|
||||
if (P_IsLocalPlayer(player))
|
||||
{
|
||||
if (player->powers[pw_underwater] == 11*TICRATE + 1
|
||||
&& player == &players[consoleplayer])
|
||||
|
|
@ -4023,11 +4014,9 @@ static void P_3dMovement(player_t *player)
|
|||
|
||||
cmd = &player->cmd;
|
||||
|
||||
if ((player->exiting || mapreset) || player->pflags & PF_STASIS || player->kartstuff[k_spinouttimer]) // pw_introcam?
|
||||
if (player->pflags & PF_STASIS || player->kartstuff[k_spinouttimer]) // pw_introcam?
|
||||
{
|
||||
cmd->forwardmove = cmd->sidemove = 0;
|
||||
if (EITHERSNEAKER(player))
|
||||
cmd->forwardmove = 50;
|
||||
}
|
||||
|
||||
if (!(player->pflags & PF_FORCESTRAFE) && !player->kartstuff[k_pogospring])
|
||||
|
|
@ -4105,9 +4094,8 @@ static void P_3dMovement(player_t *player)
|
|||
player->aiming = cmd->aiming<<FRACBITS;
|
||||
|
||||
// Forward movement
|
||||
if (!((player->exiting || mapreset) || (P_PlayerInPain(player) && !onground)))
|
||||
if (!(P_PlayerInPain(player) && !onground))
|
||||
{
|
||||
//movepushforward = cmd->forwardmove * (thrustfactor * acceleration);
|
||||
movepushforward = K_3dKartMovement(player, onground, cmd->forwardmove);
|
||||
|
||||
if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration...
|
||||
|
|
@ -4125,12 +4113,8 @@ static void P_3dMovement(player_t *player)
|
|||
movepushforward = 0;
|
||||
}
|
||||
|
||||
#ifdef ESLOPE
|
||||
totalthrust.x += P_ReturnThrustX(player->mo, movepushangle, movepushforward);
|
||||
totalthrust.y += P_ReturnThrustY(player->mo, movepushangle, movepushforward);
|
||||
#else
|
||||
P_Thrust(player->mo, movepushangle, movepushforward);
|
||||
#endif
|
||||
}
|
||||
else if (!(player->kartstuff[k_spinouttimer]))
|
||||
{
|
||||
|
|
@ -4145,15 +4129,10 @@ static void P_3dMovement(player_t *player)
|
|||
else
|
||||
movepushside = (cmd->sidemove * FRACUNIT/128) - FixedDiv(player->speed, K_GetKartSpeed(player, true));
|
||||
|
||||
#ifdef ESLOPE
|
||||
totalthrust.x += P_ReturnThrustX(player->mo, movepushsideangle, movepushside);
|
||||
totalthrust.y += P_ReturnThrustY(player->mo, movepushsideangle, movepushside);
|
||||
#else
|
||||
P_Thrust(player->mo, movepushsideangle, movepushside);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ESLOPE
|
||||
if ((totalthrust.x || totalthrust.y)
|
||||
&& player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) > FRACUNIT/2) {
|
||||
// Factor thrust to slope, but only for the part pushing up it!
|
||||
|
|
@ -4171,21 +4150,45 @@ static void P_3dMovement(player_t *player)
|
|||
}
|
||||
}
|
||||
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
K_MomentumToFacing(player);
|
||||
}
|
||||
|
||||
player->mo->momx += totalthrust.x;
|
||||
player->mo->momy += totalthrust.y;
|
||||
|
||||
if (!onground)
|
||||
{
|
||||
fixed_t airspeedcap = (50*mapobjectscale);
|
||||
fixed_t speed = R_PointToDist2(0, 0, player->mo->momx, player->mo->momy);
|
||||
const fixed_t airspeedcap = (50*mapobjectscale);
|
||||
const fixed_t speed = R_PointToDist2(0, 0, player->mo->momx, player->mo->momy);
|
||||
|
||||
if (speed > airspeedcap)
|
||||
{
|
||||
fixed_t newspeed = speed - ((speed - airspeedcap) / 32);
|
||||
fixed_t div = 32*FRACUNIT;
|
||||
fixed_t newspeed;
|
||||
|
||||
if (K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
fixed_t baserubberband = K_BotRubberband(player);
|
||||
fixed_t rubberband = FixedMul(baserubberband,
|
||||
FixedMul(baserubberband,
|
||||
FixedMul(baserubberband,
|
||||
baserubberband
|
||||
))); // This looks extremely goofy, but we need this really high, but at the same time, proportional.
|
||||
|
||||
if (rubberband > FRACUNIT)
|
||||
{
|
||||
div = FixedMul(div, rubberband);
|
||||
}
|
||||
}
|
||||
|
||||
newspeed = speed - FixedDiv((speed - airspeedcap), div);
|
||||
|
||||
player->mo->momx = FixedMul(FixedDiv(player->mo->momx, speed), newspeed);
|
||||
player->mo->momy = FixedMul(FixedDiv(player->mo->momy, speed), newspeed);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Time to ask three questions:
|
||||
// 1) Are we over topspeed?
|
||||
|
|
@ -8316,17 +8319,6 @@ void P_PlayerThink(player_t *player)
|
|||
player->playerstate = PST_DEAD;
|
||||
}
|
||||
|
||||
if (player->bot)
|
||||
{
|
||||
if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD)
|
||||
{
|
||||
if (B_CheckRespawn(player))
|
||||
player->playerstate = PST_REBORN;
|
||||
}
|
||||
if (player->playerstate == PST_REBORN)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef SEENAMES
|
||||
if (netgame && player == &players[displayplayers[0]] && !(leveltime % (TICRATE/5)) && !r_splitscreen)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ void SplitScreen_OnChange(void)
|
|||
// recompute screen size
|
||||
R_ExecuteSetViewSize();
|
||||
|
||||
if (!demo.playback && !botingame)
|
||||
if (!demo.playback)
|
||||
{
|
||||
for (i = 1; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
{
|
||||
|
|
@ -261,8 +261,6 @@ static void ChaseCam_OnChange(void)
|
|||
|
||||
static void ChaseCam2_OnChange(void)
|
||||
{
|
||||
if (botingame)
|
||||
return;
|
||||
/*if (!cv_chasecam2.value || !cv_useranalog2.value)
|
||||
CV_SetValue(&cv_analog2, 0);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -440,7 +440,7 @@ void Y_IntermissionDrawer(void)
|
|||
int y2;
|
||||
|
||||
if (data.match.rankingsmode)
|
||||
timeheader = "PWR.LV";
|
||||
timeheader = (powertype != -1 ? "PWR.LV" : "RANK");
|
||||
else
|
||||
timeheader = ((intertype == int_race || (intertype == int_match && battlecapsules)) ? "TIME" : "SCORE");
|
||||
|
||||
|
|
@ -497,7 +497,7 @@ void Y_IntermissionDrawer(void)
|
|||
|
||||
y2 = y;
|
||||
|
||||
if (playerconsole[data.match.num[i]] == 0 && server_lagless)
|
||||
if (netgame && playerconsole[data.match.num[i]] == 0 && server_lagless && !players[data.match.num[i]].bot)
|
||||
{
|
||||
static int alagles_timer = 0;
|
||||
patch_t *alagles;
|
||||
|
|
@ -533,7 +533,7 @@ void Y_IntermissionDrawer(void)
|
|||
|
||||
if (data.match.rankingsmode)
|
||||
{
|
||||
if (!clientpowerlevels[data.match.num[i]][powertype]) // No power level (splitscreen guests)
|
||||
if (powertype != -1 && !clientpowerlevels[data.match.num[i]][powertype]) // No power level (splitscreen guests)
|
||||
STRBUFCPY(strtime, "----");
|
||||
else
|
||||
{
|
||||
|
|
@ -939,9 +939,11 @@ static void K_UpdatePowerLevels(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
theirpower = PWRLVRECORD_DEF;
|
||||
if (clientpowerlevels[jpnum][powertype] != 0) // No power level acts as 5000 (used for splitscreen guests)
|
||||
theirpower = clientpowerlevels[jpnum][powertype];
|
||||
if (clientpowerlevels[jpnum][powertype] == 0) // No power level (splitscreen guests, bots)
|
||||
continue;
|
||||
|
||||
theirpower = clientpowerlevels[jpnum][powertype];
|
||||
|
||||
CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", jpnum, theirpower);
|
||||
|
||||
if (G_RaceGametype())
|
||||
|
|
@ -981,9 +983,11 @@ static void K_UpdatePowerLevels(void)
|
|||
|
||||
CONS_Debug(DBG_GAMELOGIC, "Player %d VS Player %d (griefer):\n", ipnum, jpnum);
|
||||
|
||||
theirpower = PWRLVRECORD_DEF;
|
||||
if (nospectategrief[jpnum] != 0) // No power level acts as 5000 (used for splitscreen guests)
|
||||
theirpower = nospectategrief[jpnum];
|
||||
if (nospectategrief[jpnum] == 0) // No power level (splitscreen guests, bots)
|
||||
continue;
|
||||
|
||||
theirpower = nospectategrief[jpnum];
|
||||
|
||||
CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", jpnum, theirpower);
|
||||
|
||||
diff = theirpower - yourpower;
|
||||
|
|
@ -1509,6 +1513,7 @@ static void Y_VoteStops(SINT8 pick, SINT8 level)
|
|||
void Y_VoteTicker(void)
|
||||
{
|
||||
INT32 i;
|
||||
boolean everyone_voted;
|
||||
|
||||
if (paused || P_AutoPause() || !voteclient.loaded)
|
||||
return;
|
||||
|
|
@ -1667,7 +1672,7 @@ void Y_VoteTicker(void)
|
|||
|
||||
if ((InputDown(gc_accelerate, i+1) || JoyAxis(AXISMOVE, i+1) > 0) && !pressed)
|
||||
{
|
||||
D_ModifyClientVote(voteclient.playerinfo[i].selection, i);
|
||||
D_ModifyClientVote(consoleplayer, voteclient.playerinfo[i].selection, i);
|
||||
pressed = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1682,6 +1687,8 @@ void Y_VoteTicker(void)
|
|||
|
||||
if (server)
|
||||
{
|
||||
everyone_voted = true;/* the default condition */
|
||||
|
||||
if (timer == 0)
|
||||
{
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
|
|
@ -1695,15 +1702,27 @@ void Y_VoteTicker(void)
|
|||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if ((playeringame[i] && !players[i].spectator) && votes[i] == -1)
|
||||
return;
|
||||
{
|
||||
if (players[i].bot)
|
||||
{
|
||||
if (( M_RandomFixed() % 100 ) == 0)
|
||||
D_ModifyClientVote(i, M_RandomKey(4), 0);
|
||||
}
|
||||
|
||||
if (votes[i] == -1)
|
||||
everyone_voted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timer = 0;
|
||||
if (voteendtic == -1)
|
||||
if (everyone_voted)
|
||||
{
|
||||
votenotyetpicked = false;/* don't pick vote twice */
|
||||
D_PickVote();
|
||||
timer = 0;
|
||||
if (voteendtic == -1)
|
||||
{
|
||||
votenotyetpicked = false;/* don't pick vote twice */
|
||||
D_PickVote();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue