Kart-Public/src/p_user.c

9244 lines
263 KiB
C
Raw Normal View History

2014-03-15 09:59:03 -07:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
2018-11-25 04:35:38 -08:00
// Copyright (C) 1999-2018 by Sonic Team Junior.
2014-03-15 09:59:03 -07:00
//
// 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 p_user.c
/// \brief New stuff?
/// Player related stuff.
/// Bobbing POV/weapon, movement.
/// Pending weapon.
#include "doomdef.h"
#include "i_system.h"
#include "d_event.h"
#include "d_net.h"
#include "g_game.h"
#include "p_local.h"
#include "r_main.h"
#include "s_sound.h"
#include "r_things.h"
#include "d_think.h"
#include "r_sky.h"
#include "p_setup.h"
#include "m_random.h"
#include "m_misc.h"
#include "i_video.h"
2019-09-05 14:24:23 -07:00
#include "i_joy.h"
2016-07-05 21:09:17 -07:00
#include "p_slopes.h"
2014-03-15 09:59:03 -07:00
#include "p_spec.h"
#include "r_splats.h"
#include "z_zone.h"
#include "w_wad.h"
#include "hu_stuff.h"
// We need to affect the NiGHTS hud
#include "st_stuff.h"
#include "lua_script.h"
2014-08-03 20:49:33 -07:00
#include "lua_hook.h"
2014-03-15 09:59:03 -07:00
#include "b_bot.h"
// Objectplace
#include "m_cheat.h"
2016-08-14 20:51:08 -07:00
// SRB2kart
#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems
2016-08-14 20:51:08 -07:00
#include "k_kart.h"
#include "console.h" // CON_LogMessage
2014-03-15 09:59:03 -07:00
#ifdef HW3SOUND
#include "hardware/hw3sound.h"
#endif
#ifdef HWRENDER
#include "hardware/hw_light.h"
#include "hardware/hw_main.h"
#endif
#if 0
static void P_NukeAllPlayers(player_t *player);
#endif
//
// Movement.
//
// 16 pixels of bob
//#define MAXBOB (0x10 << FRACBITS)
2014-03-15 09:59:03 -07:00
static boolean onground;
//
// P_Thrust
// Moves the given origin along a given angle.
//
void P_Thrust(mobj_t *mo, angle_t angle, fixed_t move)
{
angle >>= ANGLETOFINESHIFT;
mo->momx += FixedMul(move, FINECOSINE(angle));
if (!(twodlevel || (mo->flags2 & MF2_TWOD)))
mo->momy += FixedMul(move, FINESINE(angle));
}
#if 0
static inline void P_ThrustEvenIn2D(mobj_t *mo, angle_t angle, fixed_t move)
{
angle >>= ANGLETOFINESHIFT;
mo->momx += FixedMul(move, FINECOSINE(angle));
mo->momy += FixedMul(move, FINESINE(angle));
}
static inline void P_VectorInstaThrust(fixed_t xa, fixed_t xb, fixed_t xc, fixed_t ya, fixed_t yb, fixed_t yc,
fixed_t za, fixed_t zb, fixed_t zc, fixed_t momentum, mobj_t *mo)
{
fixed_t a1, b1, c1, a2, b2, c2, i, j, k;
a1 = xb - xa;
b1 = yb - ya;
c1 = zb - za;
a2 = xb - xc;
b2 = yb - yc;
c2 = zb - zc;
/*
// Convert to unit vectors...
a1 = FixedDiv(a1,FixedSqrt(FixedMul(a1,a1) + FixedMul(b1,b1) + FixedMul(c1,c1)));
b1 = FixedDiv(b1,FixedSqrt(FixedMul(a1,a1) + FixedMul(b1,b1) + FixedMul(c1,c1)));
c1 = FixedDiv(c1,FixedSqrt(FixedMul(c1,c1) + FixedMul(c1,c1) + FixedMul(c1,c1)));
a2 = FixedDiv(a2,FixedSqrt(FixedMul(a2,a2) + FixedMul(c2,c2) + FixedMul(c2,c2)));
b2 = FixedDiv(b2,FixedSqrt(FixedMul(a2,a2) + FixedMul(c2,c2) + FixedMul(c2,c2)));
c2 = FixedDiv(c2,FixedSqrt(FixedMul(a2,a2) + FixedMul(c2,c2) + FixedMul(c2,c2)));
*/
// Calculate the momx, momy, and momz
i = FixedMul(momentum, FixedMul(b1, c2) - FixedMul(c1, b2));
j = FixedMul(momentum, FixedMul(c1, a2) - FixedMul(a1, c2));
k = FixedMul(momentum, FixedMul(a1, b2) - FixedMul(a1, c2));
mo->momx = i;
mo->momy = j;
mo->momz = k;
}
#endif
//
// P_InstaThrust
// Moves the given origin along a given angle instantly.
//
// FIXTHIS: belongs in another file, not here
//
void P_InstaThrust(mobj_t *mo, angle_t angle, fixed_t move)
{
angle >>= ANGLETOFINESHIFT;
mo->momx = FixedMul(move, FINECOSINE(angle));
if (!(twodlevel || (mo->flags2 & MF2_TWOD)))
mo->momy = FixedMul(move,FINESINE(angle));
}
void P_InstaThrustEvenIn2D(mobj_t *mo, angle_t angle, fixed_t move)
{
angle >>= ANGLETOFINESHIFT;
mo->momx = FixedMul(move, FINECOSINE(angle));
mo->momy = FixedMul(move, FINESINE(angle));
}
// Returns a location (hard to explain - go see how it is used)
fixed_t P_ReturnThrustX(mobj_t *mo, angle_t angle, fixed_t move)
{
(void)mo;
angle >>= ANGLETOFINESHIFT;
return FixedMul(move, FINECOSINE(angle));
}
fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move)
{
(void)mo;
angle >>= ANGLETOFINESHIFT;
return FixedMul(move, FINESINE(angle));
}
2015-01-01 11:50:31 -08:00
//
// P_AutoPause
// Returns true when gameplay should be halted even if the game isn't necessarily paused.
//
boolean P_AutoPause(void)
2014-03-15 09:59:03 -07:00
{
2015-01-01 11:50:31 -08:00
// Don't pause even on menu-up or focus-lost in netgames or record attack
if (netgame || modeattacking || demo.title)
2014-03-15 09:59:03 -07:00
return false;
return ((menuactive && !demo.playback) || ( window_notinfocus && cv_pauseifunfocused.value ));
2014-03-15 09:59:03 -07:00
}
//
// P_CalcHeight
// Calculate the walking / running height adjustment
//
void P_CalcHeight(player_t *player)
{
//INT32 angle;
//fixed_t bob;
//fixed_t pviewheight;
2014-03-15 09:59:03 -07:00
mobj_t *mo = player->mo;
// Regular movement bobbing.
// Should not be calculated when not on ground (FIXTHIS?)
// OPTIMIZE: tablify angle
// Note: a LUT allows for effects
// like a ramp with low health.
/*player->bob = (FixedMul(player->rmomx,player->rmomx)
2014-03-15 09:59:03 -07:00
+ FixedMul(player->rmomy,player->rmomy))>>2;
if (player->bob > FixedMul(MAXBOB, mo->scale))
player->bob = FixedMul(MAXBOB, mo->scale);*/
2014-03-15 09:59:03 -07:00
if (!P_IsObjectOnGround(mo))
{
if (mo->eflags & MFE_VERTICALFLIP)
{
player->viewz = mo->z + mo->height - player->viewheight;
if (player->viewz < mo->floorz + FixedMul(FRACUNIT, mo->scale))
player->viewz = mo->floorz + FixedMul(FRACUNIT, mo->scale);
}
else
{
player->viewz = mo->z + player->viewheight;
if (player->viewz > mo->ceilingz - FixedMul(FRACUNIT, mo->scale))
player->viewz = mo->ceilingz - FixedMul(FRACUNIT, mo->scale);
}
return;
}
//angle = (FINEANGLES/20*localgametic)&FINEMASK;
//bob = FixedMul(player->bob/2, FINESINE(angle));
2014-03-15 09:59:03 -07:00
// move viewheight
player->viewheight = FixedMul(32 << FRACBITS, mo->scale); // default eye view height
2014-03-15 09:59:03 -07:00
/*if (player->playerstate == PST_LIVE)
2014-03-15 09:59:03 -07:00
{
player->viewheight += player->deltaviewheight;
if (player->viewheight > pviewheight)
{
player->viewheight = pviewheight;
player->deltaviewheight = 0;
}
if (player->viewheight < pviewheight/2)
{
player->viewheight = pviewheight/2;
if (player->deltaviewheight <= 0)
player->deltaviewheight = 1;
}
if (player->deltaviewheight)
{
player->deltaviewheight += FixedMul(FRACUNIT/4, mo->scale);
if (!player->deltaviewheight)
player->deltaviewheight = 1;
}
}*/
2014-03-15 09:59:03 -07:00
if (player->mo->eflags & MFE_VERTICALFLIP)
player->viewz = mo->z + mo->height - player->viewheight; //- bob
2014-03-15 09:59:03 -07:00
else
player->viewz = mo->z + player->viewheight; //+ bob
2014-03-15 09:59:03 -07:00
if (player->viewz > mo->ceilingz-FixedMul(4*FRACUNIT, mo->scale))
player->viewz = mo->ceilingz-FixedMul(4*FRACUNIT, mo->scale);
if (player->viewz < mo->floorz+FixedMul(4*FRACUNIT, mo->scale))
player->viewz = mo->floorz+FixedMul(4*FRACUNIT, mo->scale);
}
/** Decides if a player is moving.
* \param pnum The player number to test.
* \return True if the player is considered to be moving.
* \author Graue <graue@oceanbase.org>
*/
boolean P_PlayerMoving(INT32 pnum)
{
player_t *p = &players[pnum];
2014-03-18 10:56:54 -07:00
if (!Playing())
return false;
2014-03-15 09:59:03 -07:00
if (p->jointime < 5*TICRATE || p->playerstate == PST_DEAD || p->playerstate == PST_REBORN || p->spectator)
return false;
return gamestate == GS_LEVEL && p->mo && p->mo->health > 0
&& (abs(p->rmomx) >= FixedMul(FRACUNIT/2, p->mo->scale)
|| abs(p->rmomy) >= FixedMul(FRACUNIT/2, p->mo->scale)
|| abs(p->mo->momz) >= FixedMul(FRACUNIT/2, p->mo->scale)
|| p->climbing || p->powers[pw_tailsfly]
|| (p->pflags & PF_JUMPED) || (p->pflags & PF_SPINNING));
}
// P_GetNextEmerald
//
// Gets the number (0 based) of the next emerald to obtain
//
UINT8 P_GetNextEmerald(void)
{
if (!useNightsSS) // In order
{
if (!(emeralds & EMERALD1)) return 0;
if (!(emeralds & EMERALD2)) return 1;
if (!(emeralds & EMERALD3)) return 2;
if (!(emeralds & EMERALD4)) return 3;
if (!(emeralds & EMERALD5)) return 4;
if (!(emeralds & EMERALD6)) return 5;
return 6;
}
else // Depends on stage
{
if (gamemap < sstage_start || gamemap > sstage_end)
return 0;
return (UINT8)(gamemap - sstage_start);
}
}
//
// P_GiveEmerald
//
// Award an emerald upon completion
// of a special stage.
//
void P_GiveEmerald(boolean spawnObj)
{
INT32 i;
UINT8 em;
S_StartSound(NULL, sfx_cgot); // Got the emerald!
em = P_GetNextEmerald();
emeralds |= (1 << em);
if (spawnObj)
{
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
P_SetMobjState(P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z + players[i].mo->info->height, MT_GOTEMERALD),
mobjinfo[MT_GOTEMERALD].spawnstate + em);
}
}
//
// P_ResetScore
//
2015-01-01 11:50:31 -08:00
// This is called when your chain is reset.
2014-03-15 09:59:03 -07:00
void P_ResetScore(player_t *player)
{
2015-01-01 11:50:31 -08:00
// Formally a host for Chaos mode behavior
2014-03-15 09:59:03 -07:00
player->scoreadd = 0;
}
//
// P_FindLowestMare
//
// Returns the lowest open mare available
//
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
/*UINT8 P_FindLowestMare(void)
2014-03-15 09:59:03 -07:00
{
thinker_t *th;
mobj_t *mo2;
UINT8 mare = UINT8_MAX;
if (G_RaceGametype())
2014-03-15 09:59:03 -07:00
return 0;
// scan the thinkers
// to find the egg capsule with the lowest mare
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_EGGCAPSULE && mo2->health > 0)
{
const UINT8 threshold = (UINT8)mo2->threshold;
if (mare == 255)
mare = threshold;
else if (threshold < mare)
mare = threshold;
}
}
CONS_Debug(DBG_NIGHTS, "Lowest mare found: %d\n", mare);
return mare;
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
}*/
2014-03-15 09:59:03 -07:00
//
// P_FindLowestLap
//
// SRB2Kart, a similar function as above for finding the lowest lap
//
UINT8 P_FindLowestLap(void)
{
INT32 i;
UINT8 lowest = UINT8_MAX;
if (!G_RaceGametype())
return 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
if (lowest == 255)
lowest = players[i].laps;
else if (players[i].laps < lowest)
lowest = players[i].laps;
}
CONS_Debug(DBG_GAMELOGIC, "Lowest laps found: %d\n", lowest);
return lowest;
}
//
// P_FindHighestLap
//
UINT8 P_FindHighestLap(void)
{
INT32 i;
UINT8 highest = 0;
if (!G_RaceGametype())
return 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
if (players[i].laps > highest)
highest = players[i].laps;
}
CONS_Debug(DBG_GAMELOGIC, "Highest laps found: %d\n", highest);
return highest;
}
2014-03-15 09:59:03 -07:00
//
// P_TransferToNextMare
//
// Transfers the player to the next Mare.
// (Finds the lowest mare # for capsules that have not been destroyed).
// Returns true if successful, false if there is no other mare.
//
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
/*boolean P_TransferToNextMare(player_t *player)
2014-03-15 09:59:03 -07:00
{
thinker_t *th;
mobj_t *mo2;
mobj_t *closestaxis = NULL;
INT32 lowestaxisnum = -1;
UINT8 mare = P_FindLowestMare();
fixed_t dist1, dist2 = 0;
if (mare == 255)
return false;
CONS_Debug(DBG_NIGHTS, "Mare is %d\n", mare);
player->mare = mare;
// scan the thinkers
// to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_AXIS)
{
if (mo2->threshold == mare)
{
if (closestaxis == NULL)
{
closestaxis = mo2;
lowestaxisnum = mo2->health;
dist2 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y)-mo2->radius;
}
else if (mo2->health < lowestaxisnum)
{
dist1 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y)-mo2->radius;
if (dist1 < dist2)
{
closestaxis = mo2;
lowestaxisnum = mo2->health;
dist2 = dist1;
}
}
}
}
}
if (closestaxis == NULL)
return false;
P_SetTarget(&player->mo->target, closestaxis);
return true;
}
//
// P_FindAxis
//
// Given a mare and axis number, returns
// the mobj for that axis point.
static mobj_t *P_FindAxis(INT32 mare, INT32 axisnum)
{
thinker_t *th;
mobj_t *mo2;
// scan the thinkers
// to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
// Axis things are only at beginning of list.
if (!(mo2->flags2 & MF2_AXIS))
return NULL;
if (mo2->type == MT_AXIS)
{
if (mo2->health == axisnum && mo2->threshold == mare)
return mo2;
}
}
return NULL;
}
//
// P_FindAxisTransfer
//
// Given a mare and axis number, returns
// the mobj for that axis transfer point.
static mobj_t *P_FindAxisTransfer(INT32 mare, INT32 axisnum, mobjtype_t type)
{
thinker_t *th;
mobj_t *mo2;
// scan the thinkers
// to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
// Axis things are only at beginning of list.
if (!(mo2->flags2 & MF2_AXIS))
return NULL;
if (mo2->type == type)
{
if (mo2->health == axisnum && mo2->threshold == mare)
return mo2;
}
}
return NULL;
}
//
// P_TransferToAxis
//
// Finds the CLOSEST axis with the number specified.
void P_TransferToAxis(player_t *player, INT32 axisnum)
{
thinker_t *th;
mobj_t *mo2;
mobj_t *closestaxis;
INT32 mare = player->mare;
fixed_t dist1, dist2 = 0;
CONS_Debug(DBG_NIGHTS, "Transferring to axis %d\nLeveltime: %u...\n", axisnum, leveltime);
closestaxis = NULL;
// scan the thinkers
// to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_AXIS)
{
if (mo2->health == axisnum && mo2->threshold == mare)
{
if (closestaxis == NULL)
{
closestaxis = mo2;
dist2 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y)-mo2->radius;
}
else
{
dist1 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y)-mo2->radius;
if (dist1 < dist2)
{
closestaxis = mo2;
dist2 = dist1;
}
}
}
}
}
if (!closestaxis)
{
CONS_Debug(DBG_NIGHTS, "ERROR: Specified axis point to transfer to not found!\n%d\n", axisnum);
}
else
{
CONS_Debug(DBG_NIGHTS, "Transferred to axis %d, mare %d\n", closestaxis->health, closestaxis->threshold);
}
P_SetTarget(&player->mo->target, closestaxis);
}
//
// P_DeNightserizePlayer
//
// Whoops! Ran out of NiGHTS time!
//
static void P_DeNightserizePlayer(player_t *player)
{
thinker_t *th;
mobj_t *mo2;
player->pflags &= ~PF_NIGHTSMODE;
//if (player->mo->tracer)
//P_RemoveMobj(player->mo->tracer);
player->powers[pw_underwater] = 0;
player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_THOKKED|PF_SPINNING|PF_DRILLING|PF_TRANSFERTOCLOSEST);
player->secondjump = 0;
player->jumping = 0;
player->homing = 0;
player->climbing = 0;
player->mo->fuse = 0;
player->speed = 0;
P_SetTarget(&player->mo->target, NULL);
P_SetTarget(&player->axis1, P_SetTarget(&player->axis2, NULL));
player->mo->flags &= ~MF_NOGRAVITY;
player->mo->flags2 &= ~MF2_DONTDRAW;
// Restore aiming angle
if (player == &players[consoleplayer])
localaiming[0] = 0;
else if (player == &players[displayplayers[1]])
localaiming[1] = 0;
else if (player == &players[displayplayers[2]])
localaiming[2] = 0;
else if (player == &players[displayplayers[3]])
localaiming[3] = 0;
2014-03-15 09:59:03 -07:00
if (player->mo->tracer)
P_RemoveMobj(player->mo->tracer);
2016-08-14 20:51:08 -07:00
//P_SetPlayerMobjState(player->mo, S_PLAY_FALL1); // SRB2kart
2014-03-15 09:59:03 -07:00
player->pflags |= PF_NIGHTSFALL;
// If in a special stage, add some preliminary exit time.
if (G_IsSpecialStage(gamemap))
{
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].pflags & PF_NIGHTSMODE)
players[i].nightstime = 1; // force everyone else to fall too.
2018-09-22 15:59:26 -07:00
player->exiting = raceexittime+2;
2014-03-15 09:59:03 -07:00
stagefailed = true; // NIGHT OVER
}
// Check to see if the player should be killed.
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (!(mo2->type == MT_NIGHTSDRONE))
continue;
if (mo2->flags2 & MF2_AMBUSH)
2014-03-15 09:59:03 -07:00
P_DamageMobj(player->mo, NULL, NULL, 10000);
break;
}
// Restore from drowning music
P_RestoreMusic(player);
}
//
// P_NightserizePlayer
//
// NiGHTS Time!
void P_NightserizePlayer(player_t *player, INT32 nighttime)
{
INT32 oldmare;
// Bots can't be super, silly!1 :P
if (player->bot)
return;
2014-08-03 20:49:33 -07:00
if (!(player->pflags & PF_NIGHTSMODE))
{
P_SetTarget(&player->mo->tracer, P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_NIGHTSCHAR));
player->mo->tracer->destscale = player->mo->scale;
P_SetScale(player->mo->tracer, player->mo->scale);
player->mo->tracer->eflags = (player->mo->tracer->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
2014-11-11 16:55:07 -08:00
player->mo->height = player->mo->tracer->height;
2014-08-03 20:49:33 -07:00
}
2014-03-15 09:59:03 -07:00
player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_THOKKED|PF_SPINNING|PF_DRILLING);
player->homing = 0;
player->mo->fuse = 0;
player->speed = 0;
player->climbing = 0;
player->secondjump = 0;
player->powers[pw_shield] = SH_NONE;
player->mo->flags |= MF_NOGRAVITY;
player->mo->flags2 |= MF2_DONTDRAW;
player->nightstime = player->startedtime = nighttime*TICRATE;
player->bonustime = false;
P_RestoreMusic(player);
P_SetMobjState(player->mo->tracer, S_SUPERTRANS1);
if (G_RaceGametype())
2014-03-15 09:59:03 -07:00
{
if (player->drillmeter < 48*20)
player->drillmeter = 48*20;
}
else
{
if (player->drillmeter < 40*20)
player->drillmeter = 40*20;
}
oldmare = player->mare;
if (P_TransferToNextMare(player) == false)
{
INT32 i;
INT32 total_rings = 0;
P_SetTarget(&player->mo->target, NULL);
if (G_IsSpecialStage(gamemap))
{
for (i = 0; i < MAXPLAYERS; i++)
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
if (playeringame[i])
2014-03-15 09:59:03 -07:00
total_rings += players[i].health-1;
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || !players[i].mo || players[i].spectator)
continue;
players[i].texttimer = (3 * TICRATE) - 10;
players[i].textvar = 4; // Score and grades
players[i].lastmare = players[i].mare;
if (G_IsSpecialStage(gamemap))
{
players[i].finishedrings = (INT16)total_rings;
P_AddPlayerScore(player, total_rings * 50);
}
else
{
players[i].finishedrings = (INT16)(players[i].health - 1);
P_AddPlayerScore(&players[i], (players[i].health - 1) * 50);
}
// transfer scores anyway
players[i].mo->health = players[i].health = 1;
P_DoPlayerExit(&players[i]);
}
}
else if (oldmare != player->mare)
{
/// \todo Handle multi-mare special stages.
// Ring bonus
P_AddPlayerScore(player, (player->health - 1) * 50);
player->lastmare = (UINT8)oldmare;
player->texttimer = 4*TICRATE;
player->textvar = 4; // Score and grades
player->finishedrings = (INT16)(player->health - 1);
// Starting a new mare, transfer scores
player->marebegunat = leveltime;
player->mo->health = player->health = 1;
}
else
{
player->textvar = 5; // Nothing, just tells it to go to the GET n RINGS/SPHERES text in a bit
player->texttimer = 40;
// Don't show before title card
// Not consistency safe, but this only affects drawing
if (timeinmap + 40 < 110)
player->texttimer = (UINT8)(110 - timeinmap);
}
player->pflags |= PF_NIGHTSMODE;
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
}*/
2014-03-15 09:59:03 -07:00
//
// P_PlayerInPain
//
// Is player in pain??
// Checks for painstate and pw_flashing, if both found return true
//
boolean P_PlayerInPain(player_t *player)
{
// no silly, sliding isn't pain
if (!(player->pflags & PF_SLIDING) && player->mo->state == &states[player->mo->info->painstate] && player->powers[pw_flashing])
return true;
return false;
}
//
// P_DoPlayerPain
//
// Player was hit,
// put them in pain.
//
void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
{
angle_t ang;
fixed_t fallbackspeed;
2018-07-22 17:55:18 -07:00
if (inflictor && (inflictor->type != MT_PLAYER && inflictor->type != MT_ORBINAUT && inflictor->type != MT_ORBINAUT_SHIELD
2018-10-29 14:45:59 -07:00
&& inflictor->type != MT_JAWZ && inflictor->type != MT_JAWZ_DUD && inflictor->type != MT_JAWZ_SHIELD
&& inflictor->type != MT_SMK_THWOMP))
{
if (player->mo->eflags & MFE_VERTICALFLIP)
player->mo->z--;
else
player->mo->z++;
2016-07-05 21:09:17 -07:00
if (player->mo->eflags & MFE_UNDERWATER)
P_SetObjectMomZ(player->mo, FixedDiv(10511*FRACUNIT,2600*FRACUNIT), false);
else
P_SetObjectMomZ(player->mo, FixedDiv(69*FRACUNIT,10*FRACUNIT), false);
}
2014-03-15 09:59:03 -07:00
if (inflictor)
{
ang = R_PointToAngle2(inflictor->x, inflictor->y, player->mo->x, player->mo->y); // SRB2kart
//ang = R_PointToAngle2(inflictor->x-inflictor->momx, inflictor->y - inflictor->momy, player->mo->x - player->mo->momx, player->mo->y - player->mo->momy);
2014-03-15 09:59:03 -07:00
// explosion and rail rings send you farther back, making it more difficult
// to recover
if ((inflictor->flags2 & MF2_SCATTER) && source)
{
fixed_t dist = P_AproxDistance(P_AproxDistance(source->x-player->mo->x, source->y-player->mo->y), source->z-player->mo->z);
dist = FixedMul(128*FRACUNIT, inflictor->scale) - dist/4;
if (dist < FixedMul(4*FRACUNIT, inflictor->scale))
dist = FixedMul(4*FRACUNIT, inflictor->scale);
fallbackspeed = dist;
}
else if (inflictor->flags2 & MF2_EXPLOSION)
{
if (inflictor->flags2 & MF2_RAILRING)
fallbackspeed = FixedMul(38*FRACUNIT, inflictor->scale); // 7x
else
fallbackspeed = FixedMul(30*FRACUNIT, inflictor->scale); // 5x
}
else if (inflictor->flags2 & MF2_RAILRING)
fallbackspeed = FixedMul(45*FRACUNIT, inflictor->scale); // 4x
else
fallbackspeed = FixedMul(4*FRACUNIT, inflictor->scale); // the usual amount of force
}
else
{
2014-08-03 20:49:33 -07:00
ang = R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0);
2014-03-15 09:59:03 -07:00
fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale);
}
2016-07-05 21:09:17 -07:00
P_InstaThrust(player->mo, ang, fallbackspeed);
2014-03-15 09:59:03 -07:00
if (player->pflags & PF_ROPEHANG)
P_SetTarget(&player->mo->tracer, NULL);
// Point penalty for hitting a hazard during tag.
// Discourages players from intentionally hurting themselves to avoid being tagged.
/*if (gametype == GT_TAG && (!(player->pflags & PF_TAGGED) && !(player->pflags & PF_TAGIT)))
2014-03-15 09:59:03 -07:00
{
if (player->score >= 50)
player->score -= 50;
else
player->score = 0;
}*/
2014-03-15 09:59:03 -07:00
P_ResetPlayer(player);
P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
player->powers[pw_flashing] = K_GetKartFlashing(player);
2014-03-15 09:59:03 -07:00
if (player->timeshit != UINT8_MAX)
++player->timeshit;
}
//
// P_ResetPlayer
//
// Useful when you want to kill everything the player is doing.
void P_ResetPlayer(player_t *player)
{
player->pflags &= ~(PF_ROPEHANG|PF_ITEMHANG|PF_MACESPIN|PF_SPINNING|PF_JUMPED|PF_GLIDING|PF_THOKKED|PF_CARRIED);
player->jumping = 0;
player->secondjump = 0;
player->glidetime = 0;
player->homing = 0;
player->climbing = 0;
player->powers[pw_tailsfly] = 0;
player->onconveyor = 0;
player->skidtime = 0;
/*if (player-players == consoleplayer && botingame)
CV_SetValue(&cv_analog2, true);*/
2014-03-15 09:59:03 -07:00
}
//
// P_GivePlayerRings
//
// Gives rings to the player, and does any special things required.
// Call this function when you want to increment the player's health.
//
void P_GivePlayerRings(player_t *player, INT32 num_rings)
{
if (player->bot)
player = &players[consoleplayer];
if (!player->mo)
return;
player->mo->health += num_rings;
player->health += num_rings;
if (!G_IsSpecialStage(gamemap) || !useNightsSS)
player->totalring += num_rings;
//{ SRB2kart - rings don't really do anything, but we don't want the player spilling them later.
/*
2014-03-15 09:59:03 -07:00
// Can only get up to 9999 rings, sorry!
if (player->mo->health > 10000)
{
player->mo->health = 10000;
player->health = 10000;
}
else if (player->mo->health < 1)*/
2014-03-15 09:59:03 -07:00
{
player->mo->health = 1;
player->health = 1;
}
//}
2014-03-15 09:59:03 -07:00
// Now extra life bonuses are handled here instead of in P_MovePlayer, since why not?
if (!ultimatemode && !modeattacking && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives())
{
INT32 gainlives = 0;
while (player->xtralife < maxXtraLife && player->health > 100 * (player->xtralife+1))
{
++gainlives;
++player->xtralife;
}
if (gainlives)
{
P_GivePlayerLives(player, gainlives);
P_PlayLivesJingle(player);
}
}
}
//
// P_GivePlayerLives
//
// Gives the player an extra life.
// Call this function when you want to add lives to the player.
//
void P_GivePlayerLives(player_t *player, INT32 numlives)
{
player->lives += numlives;
if (player->lives > 99)
player->lives = 99;
else if (player->lives < 1)
player->lives = 1;
}
//
// P_DoSuperTransformation
//
// Transform into Super Sonic!
void P_DoSuperTransformation(player_t *player, boolean giverings)
{
2016-08-14 20:51:08 -07:00
return; // SRB2kart - this is not a thing we need
2014-03-15 09:59:03 -07:00
player->powers[pw_super] = 1;
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC) && P_IsLocalPlayer(player))
{
S_StopMusic();
2016-07-05 21:09:17 -07:00
S_ChangeMusicInternal("supers", true);
2014-03-15 09:59:03 -07:00
}
S_StartSound(NULL, sfx_supert); //let all players hear it -mattw_cfi
// Transformation animation
2016-08-14 20:51:08 -07:00
//P_SetPlayerMobjState(player->mo, S_PLAY_SUPERTRANS1);
2014-03-15 09:59:03 -07:00
player->mo->momx = player->mo->momy = player->mo->momz = 0;
2014-03-15 09:59:03 -07:00
if (giverings)
{
player->mo->health = 51;
player->health = player->mo->health;
}
// Just in case.
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC))
{
player->powers[pw_extralife] = 0;
player->powers[pw_invulnerability] = 0;
2014-08-26 20:56:30 -07:00
player->powers[pw_sneakers] = 0;
2014-03-15 09:59:03 -07:00
}
if (gametype != GT_COOP)
{
HU_SetCEchoFlags(0);
HU_SetCEchoDuration(5);
HU_DoCEcho(va("%s\\is now super.\\\\\\\\", player_names[player-players]));
}
P_PlayerFlagBurst(player, false);
}
// Adds to the player's score
void P_AddPlayerScore(player_t *player, UINT32 amount)
{
//UINT32 oldscore;
2018-06-02 07:57:43 -07:00
2018-07-01 01:36:09 -07:00
if (!(G_BattleGametype()))
return;
2014-03-15 09:59:03 -07:00
if (player->bot)
player = &players[consoleplayer];
2017-11-13 17:45:57 -08:00
if (player->exiting) // srb2kart
return;
//oldscore = player->score;
2014-03-15 09:59:03 -07:00
// Don't go above MAXSCORE.
if (player->marescore + amount < MAXSCORE)
player->marescore += amount;
2014-03-15 09:59:03 -07:00
else
player->marescore = MAXSCORE;
2014-03-15 09:59:03 -07:00
// check for extra lives every 50000 pts
/*if (!ultimatemode && !modeattacking && player->score > oldscore && player->score % 50000 < amount && (gametype == GT_COMPETITION || gametype == GT_COOP))
2014-03-15 09:59:03 -07:00
{
P_GivePlayerLives(player, (player->score/50000) - (oldscore/50000));
P_PlayLivesJingle(player);
}*/
2014-03-15 09:59:03 -07:00
// In team match, all awarded points are incremented to the team's running score.
if (gametype == GT_TEAMMATCH)
{
if (player->ctfteam == 1)
redscore += amount;
else if (player->ctfteam == 2)
bluescore += amount;
}
}
//
// P_PlayLivesJingle
//
void P_PlayLivesJingle(player_t *player)
{
if (player && !P_IsLocalPlayer(player))
return;
if (use1upSound)
S_StartSound(NULL, sfx_oneup);
else if (mariomode)
S_StartSound(NULL, sfx_marioa);
else
{
if (player)
player->powers[pw_extralife] = extralifetics + 1;
S_StopMusic(); // otherwise it won't restart if this is done twice in a row
2016-07-05 21:09:17 -07:00
S_ChangeMusicInternal("xtlife", false);
2014-03-15 09:59:03 -07:00
}
}
void P_PlayRinglossSound(mobj_t *source)
{
sfxenum_t key = P_RandomKey(2);
if (cv_kartvoices.value)
S_StartSound(source, (mariomode) ? sfx_mario8 : sfx_khurt1 + key);
else
S_StartSound(source, sfx_slip);
}
void P_PlayDeathSound(mobj_t *source)
{
S_StartSound(source, sfx_s3k35);
}
void P_PlayVictorySound(mobj_t *source)
{
if (cv_kartvoices.value)
S_StartSound(source, sfx_kwin);
}
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
//
// P_EndingMusic
//
// Consistently sets ending music!
//
boolean P_EndingMusic(player_t *player)
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
{
char buffer[9];
boolean looping = true;
INT32 bestlocalpos;
player_t *bestlocalplayer;
if (!P_IsLocalPlayer(player)) // Only applies to a local player
return false;
if (multiplayer && demo.playback) // Don't play this in multiplayer replays
return false;
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
// Event - Level Finish
// Check for if this is valid or not
if (splitscreen)
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
{
if (!((players[displayplayers[0]].exiting || (players[displayplayers[0]].pflags & PF_TIMEOVER))
|| (players[displayplayers[1]].exiting || (players[displayplayers[1]].pflags & PF_TIMEOVER))
|| ((splitscreen < 2) && (players[displayplayers[2]].exiting || (players[displayplayers[2]].pflags & PF_TIMEOVER)))
|| ((splitscreen < 3) && (players[displayplayers[3]].exiting || (players[displayplayers[3]].pflags & PF_TIMEOVER)))))
return false;
bestlocalplayer = &players[displayplayers[0]];
bestlocalpos = ((players[displayplayers[0]].pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : players[displayplayers[0]].kartstuff[k_position]);
#define setbests(p) \
if (((players[p].pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : players[p].kartstuff[k_position]) < bestlocalpos) \
{ \
bestlocalplayer = &players[p]; \
bestlocalpos = ((players[p].pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : players[p].kartstuff[k_position]); \
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
}
setbests(displayplayers[1]);
if (splitscreen > 1)
setbests(displayplayers[2]);
if (splitscreen > 2)
setbests(displayplayers[3]);
#undef setbests
}
else
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
{
if (!(player->exiting || (player->pflags & PF_TIMEOVER)))
return false;
bestlocalplayer = player;
bestlocalpos = ((player->pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : player->kartstuff[k_position]);
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
}
if (G_RaceGametype() && bestlocalpos == MAXPLAYERS+1)
2018-10-23 14:48:09 -07:00
sprintf(buffer, "k*fail"); // F-Zero death results theme
else
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
{
if (bestlocalpos == 1)
sprintf(buffer, "k*win");
else if (K_IsPlayerLosing(bestlocalplayer))
sprintf(buffer, "k*lose");
else
sprintf(buffer, "k*ok");
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
}
S_SpeedMusic(1.0f);
if (G_RaceGametype())
buffer[1] = 'r';
else if (G_BattleGametype())
{
buffer[1] = 'b';
looping = false;
}
S_ChangeMusicInternal(buffer, looping);
return true;
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
}
2014-03-15 09:59:03 -07:00
//
// P_RestoreMusic
//
// Restores music after some special music change
//
void P_RestoreMusic(player_t *player)
{
if (!P_IsLocalPlayer(player)) // Only applies to a local player
return;
S_SpeedMusic(1.0f);
2018-07-21 21:31:02 -07:00
// Event - HERE COMES A NEW CHALLENGER
if (mapreset)
{
S_ChangeMusicInternal("chalng", false); //S_StopMusic();
return;
}
// Event - Level Ending
if (P_EndingMusic(player))
2014-03-15 09:59:03 -07:00
return;
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
// Event - Level Start
2018-06-28 18:19:58 -07:00
if (leveltime < (starttime + (TICRATE/2)))
S_ChangeMusicInternal((encoremode ? "estart" : "kstart"), false); //S_StopMusic();
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
else // see also where time overs are handled - search for "lives = 2" in this file
{
INT32 wantedmus = 0; // 0 is level music, 1 is invincibility, 2 is grow
if (splitscreen)
{
INT32 bestlocaltimer = 1;
#define setbests(p) \
if (players[p].playerstate == PST_LIVE) \
{ \
if (players[p].kartstuff[k_growshrinktimer] > bestlocaltimer) \
{ wantedmus = 2; bestlocaltimer = players[p].kartstuff[k_growshrinktimer]; } \
else if (players[p].kartstuff[k_invincibilitytimer] > bestlocaltimer) \
{ wantedmus = 1; bestlocaltimer = players[p].kartstuff[k_invincibilitytimer]; } \
}
setbests(displayplayers[0]);
setbests(displayplayers[1]);
if (splitscreen > 1)
setbests(displayplayers[2]);
if (splitscreen > 2)
setbests(displayplayers[3]);
#undef setbests
}
else
{
if (player->playerstate == PST_LIVE)
{
if (player->kartstuff[k_growshrinktimer] > 1)
wantedmus = 2;
else if (player->kartstuff[k_invincibilitytimer] > 1)
wantedmus = 1;
}
}
// Item - Grow
if (wantedmus == 2)
S_ChangeMusicInternal("kgrow", true);
// Item - Invincibility
else if (wantedmus == 1)
S_ChangeMusicInternal("kinvnc", true);
else
{
#if 0
// Event - Final Lap
// Still works for GME, but disabled for consistency
if (G_RaceGametype() && player->laps >= (UINT8)(cv_numlaps.value - 1))
S_SpeedMusic(1.2f);
#endif
S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
}
2014-04-13 22:14:58 -07:00
}
2014-03-15 09:59:03 -07:00
}
2014-08-03 20:49:33 -07:00
//
// P_IsObjectInGoop
//
// Returns true if the object is inside goop water.
// (Spectators and objects otherwise without gravity cannot have goop gravity!)
//
boolean P_IsObjectInGoop(mobj_t *mo)
{
if (mo->player && mo->player->spectator)
return false;
if (mo->flags & MF_NOGRAVITY)
return false;
return ((mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER)) == (MFE_UNDERWATER|MFE_GOOWATER));
}
2014-03-15 09:59:03 -07:00
//
// P_IsObjectOnGround
//
// Returns true if the player is
// on the ground. Takes reverse
// gravity and FOFs into account.
//
boolean P_IsObjectOnGround(mobj_t *mo)
{
2014-08-03 20:49:33 -07:00
if (P_IsObjectInGoop(mo))
2014-03-15 09:59:03 -07:00
{
2014-08-03 20:49:33 -07:00
/*
2014-03-15 09:59:03 -07:00
// It's a crazy hack that checking if you're on the ground
// would actually CHANGE your position and momentum,
if (mo->z < mo->floorz)
{
mo->z = mo->floorz;
mo->momz = 0;
}
else if (mo->z + mo->height > mo->ceilingz)
{
mo->z = mo->ceilingz - mo->height;
mo->momz = 0;
}
2014-08-03 20:49:33 -07:00
*/
2014-03-15 09:59:03 -07:00
// but I don't want you to ever 'stand' while submerged in goo.
// You're in constant vertical momentum, even if you get stuck on something.
// No exceptions.
return false;
}
if (mo->eflags & MFE_VERTICALFLIP)
{
if (mo->z+mo->height >= mo->ceilingz)
return true;
}
else
{
if (mo->z <= mo->floorz)
return true;
}
return false;
}
//
// P_IsObjectOnGroundIn
//
// Returns true if the player is
// on the ground in a specific sector. Takes reverse
// gravity and FOFs into account.
//
boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec)
{
ffloor_t *rover;
// Is the object in reverse gravity?
if (mo->eflags & MFE_VERTICALFLIP)
{
// Detect if the player is on the ceiling.
2016-07-05 21:09:17 -07:00
if (mo->z+mo->height >= P_GetSpecialTopZ(mo, sec, sec))
2014-03-15 09:59:03 -07:00
return true;
// Otherwise, detect if the player is on the bottom of a FOF.
else
{
for (rover = sec->ffloors; rover; rover = rover->next)
{
// If the FOF doesn't exist, continue.
if (!(rover->flags & FF_EXISTS))
continue;
// If the FOF is configured to let the object through, continue.
if (!((rover->flags & FF_BLOCKPLAYER && mo->player)
|| (rover->flags & FF_BLOCKOTHERS && !mo->player)))
2014-03-15 09:59:03 -07:00
continue;
// If the the platform is intangible from below, continue.
2014-03-15 09:59:03 -07:00
if (rover->flags & FF_PLATFORM)
continue;
// If the FOF is a water block, continue. (Unnecessary check?)
if (rover->flags & FF_SWIMMABLE)
continue;
// Actually check if the player is on the suitable FOF.
2016-07-05 21:09:17 -07:00
if (mo->z+mo->height == P_GetSpecialBottomZ(mo, sectors + rover->secnum, sec))
2014-03-15 09:59:03 -07:00
return true;
}
}
}
// Nope!
else
{
// Detect if the player is on the floor.
2016-07-05 21:09:17 -07:00
if (mo->z <= P_GetSpecialBottomZ(mo, sec, sec))
2014-03-15 09:59:03 -07:00
return true;
// Otherwise, detect if the player is on the top of a FOF.
else
{
for (rover = sec->ffloors; rover; rover = rover->next)
{
// If the FOF doesn't exist, continue.
if (!(rover->flags & FF_EXISTS))
continue;
// If the FOF is configured to let the object through, continue.
if (!((rover->flags & FF_BLOCKPLAYER && mo->player)
|| (rover->flags & FF_BLOCKOTHERS && !mo->player)))
2014-03-15 09:59:03 -07:00
continue;
// If the the platform is intangible from above, continue.
2014-03-15 09:59:03 -07:00
if (rover->flags & FF_REVERSEPLATFORM)
continue;
// If the FOF is a water block, continue. (Unnecessary check?)
if (rover->flags & FF_SWIMMABLE)
continue;
// Actually check if the player is on the suitable FOF.
2016-07-05 21:09:17 -07:00
if (mo->z == P_GetSpecialTopZ(mo, sectors + rover->secnum, sec))
2014-03-15 09:59:03 -07:00
return true;
}
}
}
return false;
}
//
// P_IsObjectOnRealGround
//
// Helper function for T_EachTimeThinker
// Like P_IsObjectOnGroundIn, except ONLY THE REAL GROUND IS CONSIDERED, NOT FOFS
// I'll consider whether to make this a more globally accessible function or whatever in future
// -- Monster Iestyn
//
// Really simple, but personally I think it's also incredibly helpful. I think this is fine in p_user.c
// -- Sal
boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec)
{
// Is the object in reverse gravity?
if (mo->eflags & MFE_VERTICALFLIP)
{
// Detect if the player is on the ceiling.
if (mo->z+mo->height >= P_GetSpecialTopZ(mo, sec, sec))
return true;
}
// Nope!
else
{
// Detect if the player is on the floor.
if (mo->z <= P_GetSpecialBottomZ(mo, sec, sec))
return true;
}
return false;
}
2014-03-15 09:59:03 -07:00
//
// P_SetObjectMomZ
//
// Sets the player momz appropriately.
// Takes reverse gravity into account.
//
void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative)
{
if (mo->eflags & MFE_VERTICALFLIP)
value = -value;
if (mo->scale != FRACUNIT)
value = FixedMul(value, mo->scale);
if (relative)
mo->momz += value;
else
mo->momz = value;
}
//
// P_GetPlayerHeight
//
// Returns the height
// of the player.
//
fixed_t P_GetPlayerHeight(player_t *player)
{
return FixedMul(player->mo->info->height, player->mo->scale);
}
//
// P_GetPlayerSpinHeight
//
// Returns the 'spin height'
// of the player.
//
fixed_t P_GetPlayerSpinHeight(player_t *player)
{
return FixedMul(FixedMul(player->mo->info->height, player->mo->scale),2*FRACUNIT/3);
}
//
// P_IsLocalPlayer
//
// Returns true if player is
// on the local machine.
//
boolean P_IsLocalPlayer(player_t *player)
{
UINT8 i;
if (player == &players[consoleplayer])
return true;
else if (splitscreen)
{
for (i = 1; i <= splitscreen; i++) // Skip P1
{
if (player == &players[displayplayers[i]])
return true;
}
}
return false;
}
//
// P_IsDisplayPlayer
//
// Returns true if player is
// currently being watched.
//
boolean P_IsDisplayPlayer(player_t *player)
{
UINT8 i;
for (i = 0; i <= splitscreen; i++) // DON'T skip P1
{
if (player == &players[displayplayers[i]])
return true;
}
return false;
2014-03-15 09:59:03 -07:00
}
//
// P_SpawnShieldOrb
//
// Spawns the shield orb on the player
// depending on which shield they are
// supposed to have.
//
void P_SpawnShieldOrb(player_t *player)
{
mobjtype_t orbtype;
thinker_t *th;
mobj_t *shieldobj, *ov;
#ifdef PARANOIA
if (!player->mo)
I_Error("P_SpawnShieldOrb: player->mo is NULL!\n");
#endif
if (player->powers[pw_shield] & SH_FORCE)
orbtype = MT_BLUEORB;
else switch (player->powers[pw_shield] & SH_NOSTACK)
{
case SH_JUMP:
orbtype = MT_WHITEORB;
break;
case SH_ATTRACT:
orbtype = MT_YELLOWORB;
break;
case SH_ELEMENTAL:
orbtype = MT_GREENORB;
break;
case SH_BOMB:
orbtype = MT_BLACKORB;
break;
case SH_PITY:
orbtype = MT_PITYORB;
break;
default:
return;
}
// blaze through the thinkers to see if an orb already exists!
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
shieldobj = (mobj_t *)th;
if (shieldobj->type == orbtype && shieldobj->target == player->mo)
P_RemoveMobj(shieldobj); //kill the old one(s)
}
shieldobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, orbtype);
P_SetTarget(&shieldobj->target, player->mo);
shieldobj->color = (UINT8)shieldobj->info->painchance;
if (shieldobj->info->seestate)
{
ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
P_SetTarget(&ov->target, shieldobj);
P_SetMobjState(ov, shieldobj->info->seestate);
}
if (shieldobj->info->meleestate)
{
ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
P_SetTarget(&ov->target, shieldobj);
P_SetMobjState(ov, shieldobj->info->meleestate);
}
if (shieldobj->info->missilestate)
{
ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
P_SetTarget(&ov->target, shieldobj);
P_SetMobjState(ov, shieldobj->info->missilestate);
}
if (player->powers[pw_shield] & SH_FORCE)
{
//Copy and pasted from P_ShieldLook in p_mobj.c
2014-08-03 20:49:33 -07:00
shieldobj->movecount = (player->powers[pw_shield] & 0xFF);
if (shieldobj->movecount < 1)
{
if (shieldobj->info->painstate)
P_SetMobjState(shieldobj,shieldobj->info->painstate);
else
shieldobj->flags2 |= MF2_SHADOW;
}
}
2014-03-15 09:59:03 -07:00
}
//
// P_SpawnGhostMobj
//
// Spawns a ghost object on the player
//
mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
{
mobj_t *ghost;
ghost = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_GHOST);
P_SetScale(ghost, mobj->scale);
ghost->destscale = mobj->scale;
if (mobj->eflags & MFE_VERTICALFLIP)
{
ghost->eflags |= MFE_VERTICALFLIP;
ghost->z += mobj->height - ghost->height;
}
ghost->color = mobj->color;
ghost->colorized = mobj->colorized; // Kart: they should also be colorized if their origin is
2014-03-15 09:59:03 -07:00
if (mobj->player)
ghost->angle = mobj->player->frameangle;
else
ghost->angle = mobj->angle;
2014-03-15 09:59:03 -07:00
ghost->sprite = mobj->sprite;
ghost->frame = mobj->frame;
ghost->tics = -1;
ghost->frame &= ~FF_TRANSMASK;
ghost->frame |= tr_trans50<<FF_TRANSSHIFT;
ghost->fuse = ghost->info->damage;
ghost->skin = mobj->skin;
if (mobj->flags2 & MF2_OBJECTFLIP)
ghost->flags |= MF2_OBJECTFLIP;
if (!(mobj->flags & MF_DONTENCOREMAP))
mobj->flags &= ~MF_DONTENCOREMAP;
2014-03-15 09:59:03 -07:00
return ghost;
}
//
// P_DoPlayerExit
//
// Player exits the map via sector trigger
void P_DoPlayerExit(player_t *player)
{
2018-07-21 21:31:02 -07:00
if (player->exiting || mapreset)
2014-03-15 09:59:03 -07:00
return;
if (P_IsLocalPlayer(player) && (!player->spectator && !demo.playback))
legitimateexit = true;
if (G_RaceGametype()) // If in Race Mode, allow
2014-03-15 09:59:03 -07:00
{
player->exiting = raceexittime+2;
K_KartUpdatePosition(player);
if (cv_kartvoices.value)
{
if (P_IsLocalPlayer(player))
{
sfxenum_t sfx_id;
if (K_IsPlayerLosing(player))
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_klose].skinsound];
else
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_kwin].skinsound];
2018-09-22 17:15:12 -07:00
S_StartSound(NULL, sfx_id);
}
else
{
if (K_IsPlayerLosing(player))
S_StartSound(player->mo, sfx_klose);
else
S_StartSound(player->mo, sfx_kwin);
}
}
2017-02-12 20:24:49 -08:00
if (cv_inttime.value > 0)
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
P_EndingMusic(player);
2017-02-12 20:24:49 -08:00
// SRB2kart 120217
//if (!exitcountdown)
//exitcountdown = racecountdown + 8*TICRATE;
2014-03-15 09:59:03 -07:00
if (P_CheckRacers())
2018-09-22 15:59:26 -07:00
player->exiting = raceexittime+1;
2014-03-15 09:59:03 -07:00
}
else if (G_BattleGametype()) // Battle Mode exiting
{
2018-09-22 15:59:26 -07:00
player->exiting = battleexittime+1;
P_EndingMusic(player);
}
2014-03-15 09:59:03 -07:00
else
2018-09-22 15:59:26 -07:00
player->exiting = raceexittime+2; // Accidental death safeguard???
2014-03-15 09:59:03 -07:00
2014-08-03 20:49:33 -07:00
//player->pflags &= ~PF_GLIDING;
2016-08-14 20:51:08 -07:00
/* // SRB2kart - don't need
2014-08-03 20:49:33 -07:00
if (player->climbing)
{
player->climbing = 0;
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
}
2016-08-14 20:51:08 -07:00
*/
2014-03-15 09:59:03 -07:00
player->powers[pw_underwater] = 0;
player->powers[pw_spacetime] = 0;
player->kartstuff[k_cardanimation] = 0; // srb2kart: reset battle animation
2014-03-15 09:59:03 -07:00
2019-02-16 16:29:51 -08:00
if (player == &players[consoleplayer])
demo.savebutton = leveltime;
2019-02-16 16:29:51 -08:00
2017-11-13 17:45:57 -08:00
/*if (playeringame[player-players] && netgame && !circuitmap)
CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]);*/
2014-03-15 09:59:03 -07:00
}
#define SPACESPECIAL 12
2014-08-03 20:49:33 -07:00
boolean P_InSpaceSector(mobj_t *mo) // Returns true if you are in space
2014-03-15 09:59:03 -07:00
{
sector_t *sector = mo->subsector->sector;
fixed_t topheight, bottomheight;
2014-03-15 09:59:03 -07:00
if (GETSECSPECIAL(sector->special, 1) == SPACESPECIAL)
return true;
if (sector->ffloors)
{
ffloor_t *rover;
for (rover = sector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS))
continue;
2014-03-15 09:59:03 -07:00
if (GETSECSPECIAL(rover->master->frontsector->special, 1) != SPACESPECIAL)
continue;
#ifdef ESLOPE
topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, mo->x, mo->y) : *rover->topheight;
bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, mo->x, mo->y) : *rover->bottomheight;
#else
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
#endif
2014-03-15 09:59:03 -07:00
if (mo->z + (mo->height/2) > topheight)
2014-03-15 09:59:03 -07:00
continue;
if (mo->z + (mo->height/2) < bottomheight)
2014-03-15 09:59:03 -07:00
continue;
return true;
}
}
return false; // No vacuum here, Captain!
}
2014-08-03 20:49:33 -07:00
boolean P_InQuicksand(mobj_t *mo) // Returns true if you are in quicksand
2014-03-15 09:59:03 -07:00
{
sector_t *sector = mo->subsector->sector;
fixed_t topheight, bottomheight;
2014-03-15 09:59:03 -07:00
fixed_t flipoffset = ((mo->eflags & MFE_VERTICALFLIP) ? (mo->height/2) : 0);
2014-03-15 09:59:03 -07:00
if (sector->ffloors)
{
ffloor_t *rover;
for (rover = sector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS))
continue;
if (!(rover->flags & FF_QUICKSAND))
continue;
#ifdef ESLOPE
topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, mo->x, mo->y) : *rover->topheight;
bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, mo->x, mo->y) : *rover->bottomheight;
#else
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
#endif
if (mo->z + flipoffset > topheight)
2014-03-15 09:59:03 -07:00
continue;
if (mo->z + (mo->height/2) + flipoffset < bottomheight)
2014-03-15 09:59:03 -07:00
continue;
return true;
}
}
return false; // No sand here, Captain!
}
static void P_CheckBustableBlocks(player_t *player)
{
msecnode_t *node;
fixed_t oldx;
fixed_t oldy;
if ((netgame || multiplayer) && player->spectator)
return;
oldx = player->mo->x;
oldy = player->mo->y;
P_UnsetThingPosition(player->mo);
player->mo->x += player->mo->momx;
player->mo->y += player->mo->momy;
P_SetThingPosition(player->mo);
for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next)
2014-03-15 09:59:03 -07:00
{
if (!node->m_sector)
break;
if (node->m_sector->ffloors)
{
ffloor_t *rover;
fixed_t topheight, bottomheight;
2014-03-15 09:59:03 -07:00
for (rover = node->m_sector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS)) continue;
if ((rover->flags & FF_BUSTUP)/* && !rover->master->frontsector->crumblestate*/)
{
// If it's an FF_SPINBUST, you have to either be jumping, or coming down
// onto the top from a spin.
if (rover->flags & FF_SPINBUST && ((!(player->pflags & PF_JUMPED) && !(player->pflags & PF_SPINNING)) || (player->pflags & PF_STARTDASH)))
continue;
// if it's not an FF_SHATTER, you must be spinning (and not jumping)
// or have Knuckles's abilities (or Super Sonic)
// ...or are drilling in NiGHTS (or Metal Sonic)
if (!(rover->flags & FF_SHATTER) && !(rover->flags & FF_SPINBUST)
&& !((player->pflags & PF_SPINNING) && !(player->pflags & PF_JUMPED))
2019-05-01 21:39:49 -07:00
&& (/*player->charability != CA_GLIDEANDCLIMB &&*/ !player->powers[pw_super])
2014-03-15 09:59:03 -07:00
&& !(player->pflags & PF_DRILLING) && !metalrecording)
continue;
// Only Knuckles can break this rock...
2019-05-01 21:39:49 -07:00
/*if (!(rover->flags & FF_SHATTER) && (rover->flags & FF_ONLYKNUX) && !(player->charability == CA_GLIDEANDCLIMB))
continue;*/
2014-03-15 09:59:03 -07:00
topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
2014-03-15 09:59:03 -07:00
// Height checks
if (rover->flags & FF_SHATTERBOTTOM)
{
if (player->mo->z+player->mo->momz + player->mo->height < bottomheight)
2014-03-15 09:59:03 -07:00
continue;
if (player->mo->z+player->mo->height > bottomheight)
2014-03-15 09:59:03 -07:00
continue;
}
else if (rover->flags & FF_SPINBUST)
{
if (player->mo->z+player->mo->momz > topheight)
2014-03-15 09:59:03 -07:00
continue;
if (player->mo->z + player->mo->height < bottomheight)
2014-03-15 09:59:03 -07:00
continue;
}
else if (rover->flags & FF_SHATTER)
{
if (player->mo->z + player->mo->momz > topheight)
2014-03-15 09:59:03 -07:00
continue;
if (player->mo->z+player->mo->momz + player->mo->height < bottomheight)
2014-03-15 09:59:03 -07:00
continue;
}
else
{
if (player->mo->z >= topheight)
2014-03-15 09:59:03 -07:00
continue;
if (player->mo->z + player->mo->height < bottomheight)
2014-03-15 09:59:03 -07:00
continue;
}
// Impede the player's fall a bit
if (((rover->flags & FF_SPINBUST) || (rover->flags & FF_SHATTER)) && player->mo->z >= topheight)
2014-03-15 09:59:03 -07:00
player->mo->momz >>= 1;
else if (rover->flags & FF_SHATTER)
{
player->mo->momx >>= 1;
player->mo->momy >>= 1;
}
//if (metalrecording)
// G_RecordBustup(rover);
EV_CrumbleChain(node->m_sector, rover);
// Run a linedef executor??
if (rover->master->flags & ML_EFFECT5)
P_LinedefExecute((INT16)(P_AproxDistance(rover->master->dx, rover->master->dy)>>FRACBITS), player->mo, node->m_sector);
goto bustupdone;
}
}
}
}
bustupdone:
P_UnsetThingPosition(player->mo);
player->mo->x = oldx;
player->mo->y = oldy;
P_SetThingPosition(player->mo);
}
static void P_CheckBouncySectors(player_t *player)
{
msecnode_t *node;
fixed_t oldx;
fixed_t oldy;
fixed_t oldz;
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
vector3_t momentum;
#endif
2014-03-15 09:59:03 -07:00
oldx = player->mo->x;
oldy = player->mo->y;
oldz = player->mo->z;
P_UnsetThingPosition(player->mo);
player->mo->x += player->mo->momx;
player->mo->y += player->mo->momy;
player->mo->z += player->mo->momz;
P_SetThingPosition(player->mo);
for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next)
2014-03-15 09:59:03 -07:00
{
if (!node->m_sector)
break;
if (node->m_sector->ffloors)
{
ffloor_t *rover;
boolean top = true;
2016-07-05 21:09:17 -07:00
fixed_t topheight, bottomheight;
2014-03-15 09:59:03 -07:00
for (rover = node->m_sector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS))
continue; // FOFs should not be bouncy if they don't even "exist"
if (GETSECSPECIAL(rover->master->frontsector->special, 1) != 15)
continue; // this sector type is required for FOFs to be bouncy
2016-07-05 21:09:17 -07:00
topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
if (player->mo->z > topheight)
2014-03-15 09:59:03 -07:00
continue;
2016-07-05 21:09:17 -07:00
if (player->mo->z + player->mo->height < bottomheight)
2014-03-15 09:59:03 -07:00
continue;
2016-07-05 21:09:17 -07:00
if (oldz < P_GetFOFTopZ(player->mo, node->m_sector, rover, oldx, oldy, NULL)
&& oldz + player->mo->height > P_GetFOFBottomZ(player->mo, node->m_sector, rover, oldx, oldy, NULL))
2014-03-15 09:59:03 -07:00
top = false;
{
fixed_t linedist;
linedist = P_AproxDistance(rover->master->v1->x-rover->master->v2->x, rover->master->v1->y-rover->master->v2->y);
linedist = FixedDiv(linedist,100*FRACUNIT);
if (top)
{
fixed_t newmom;
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
pslope_t *slope;
if (abs(oldz - topheight) < abs(oldz + player->mo->height - bottomheight)) { // Hit top
slope = *rover->t_slope;
} else { // Hit bottom
slope = *rover->b_slope;
}
momentum.x = player->mo->momx;
momentum.y = player->mo->momy;
momentum.z = player->mo->momz*2;
if (slope)
P_ReverseQuantizeMomentumToSlope(&momentum, slope);
2016-07-05 21:09:17 -07:00
newmom = momentum.z = -FixedMul(momentum.z,linedist)/2;
#else
2014-03-15 09:59:03 -07:00
newmom = -FixedMul(player->mo->momz,linedist);
2016-07-05 21:09:17 -07:00
#endif
2014-03-15 09:59:03 -07:00
if (abs(newmom) < (linedist*2))
{
goto bouncydone;
}
if (!(rover->master->flags & ML_BOUNCY))
{
if (newmom > 0)
{
if (newmom < 8*FRACUNIT)
newmom = 8*FRACUNIT;
}
else if (newmom > -8*FRACUNIT && newmom != 0)
newmom = -8*FRACUNIT;
}
if (newmom > P_GetPlayerHeight(player)/2)
newmom = P_GetPlayerHeight(player)/2;
else if (newmom < -P_GetPlayerHeight(player)/2)
newmom = -P_GetPlayerHeight(player)/2;
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
momentum.z = newmom*2;
if (slope)
P_QuantizeMomentumToSlope(&momentum, slope);
player->mo->momx = momentum.x;
player->mo->momy = momentum.y;
player->mo->momz = momentum.z/2;
#else
2014-03-15 09:59:03 -07:00
player->mo->momz = newmom;
2016-07-05 21:09:17 -07:00
#endif
2014-03-15 09:59:03 -07:00
if (player->pflags & PF_SPINNING)
{
player->pflags &= ~PF_SPINNING;
player->pflags |= PF_JUMPED;
player->pflags |= PF_THOKKED;
}
}
else
{
player->mo->momx = -FixedMul(player->mo->momx,linedist);
player->mo->momy = -FixedMul(player->mo->momy,linedist);
if (player->pflags & PF_SPINNING)
{
player->pflags &= ~PF_SPINNING;
player->pflags |= PF_JUMPED;
player->pflags |= PF_THOKKED;
}
}
if ((player->pflags & PF_SPINNING) && player->speed < FixedMul(1<<FRACBITS, player->mo->scale) && player->mo->momz)
{
player->pflags &= ~PF_SPINNING;
player->pflags |= PF_JUMPED;
}
goto bouncydone;
}
}
}
}
bouncydone:
P_UnsetThingPosition(player->mo);
player->mo->x = oldx;
player->mo->y = oldy;
player->mo->z = oldz;
P_SetThingPosition(player->mo);
}
static void P_CheckQuicksand(player_t *player)
{
ffloor_t *rover;
fixed_t sinkspeed, friction;
fixed_t topheight, bottomheight;
2014-03-15 09:59:03 -07:00
if (!(player->mo->subsector->sector->ffloors && player->mo->momz <= 0))
return;
for (rover = player->mo->subsector->sector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS)) continue;
if (!(rover->flags & FF_QUICKSAND))
continue;
#ifdef ESLOPE
topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight;
bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight;
#else
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
#endif
if (topheight >= player->mo->z && bottomheight < player->mo->z + player->mo->height)
2014-03-15 09:59:03 -07:00
{
sinkspeed = abs(rover->master->v1->x - rover->master->v2->x)>>1;
sinkspeed = FixedDiv(sinkspeed,TICRATE*FRACUNIT);
if (player->mo->eflags & MFE_VERTICALFLIP)
{
fixed_t ceilingheight = P_GetCeilingZ(player->mo, player->mo->subsector->sector, player->mo->x, player->mo->y, NULL);
player->mo->z += sinkspeed;
2014-03-15 09:59:03 -07:00
if (player->mo->z + player->mo->height >= ceilingheight)
player->mo->z = ceilingheight - player->mo->height;
}
else
{
fixed_t floorheight = P_GetFloorZ(player->mo, player->mo->subsector->sector, player->mo->x, player->mo->y, NULL);
player->mo->z -= sinkspeed;
if (player->mo->z <= floorheight)
player->mo->z = floorheight;
}
2014-03-15 09:59:03 -07:00
friction = abs(rover->master->v1->y - rover->master->v2->y)>>6;
player->mo->momx = FixedMul(player->mo->momx, friction);
player->mo->momy = FixedMul(player->mo->momy, friction);
}
}
}
//
// P_CheckSneakerAndLivesTimer
//
// Restores music from sneaker and life fanfares
//
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
/* // SRB2kart - Can't drown.
2014-03-15 09:59:03 -07:00
static void P_CheckSneakerAndLivesTimer(player_t *player)
{
if ((player->powers[pw_underwater] <= 11*TICRATE + 1)
&& (player->powers[pw_underwater] > 1))
return; // don't restore music if drowning music is playing
if (player->powers[pw_extralife] == 1) // Extra Life!
P_RestoreMusic(player);
//if (player->powers[pw_sneakers] == 1) // SRB2kart
// P_RestoreMusic(player);
2014-03-15 09:59:03 -07:00
}
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
*/
2014-03-15 09:59:03 -07:00
//
// P_CheckUnderwaterAndSpaceTimer
//
// Restores music from underwater and space warnings, and handles number generation
//
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
/* // SRB2kart - Can't drown.
2014-03-15 09:59:03 -07:00
static void P_CheckUnderwaterAndSpaceTimer(player_t *player)
{
fixed_t height;
mobj_t *numbermobj = NULL;
if (player->mo->eflags & MFE_VERTICALFLIP)
height = player->mo->z - FixedMul(8*FRACUNIT - mobjinfo[MT_DROWNNUMBERS].height, player->mo->scale);
else
height = player->mo->z + player->mo->height + FixedMul(8*FRACUNIT, player->mo->scale);
if (player->powers[pw_underwater] == 11*TICRATE + 1 || player->powers[pw_spacetime] == 11*TICRATE + 1)
{
numbermobj = P_SpawnMobj(player->mo->x, player->mo->y, height, MT_DROWNNUMBERS);
P_SetMobjState(numbermobj, numbermobj->info->spawnstate+5);
}
else if (player->powers[pw_underwater] == 9*TICRATE + 1 || player->powers[pw_spacetime] == 9*TICRATE + 1)
{
numbermobj = P_SpawnMobj(player->mo->x, player->mo->y, height, MT_DROWNNUMBERS);
P_SetMobjState(numbermobj, numbermobj->info->spawnstate+4);
}
else if (player->powers[pw_underwater] == 7*TICRATE + 1 || player->powers[pw_spacetime] == 7*TICRATE + 1)
{
numbermobj = P_SpawnMobj(player->mo->x, player->mo->y, height, MT_DROWNNUMBERS);
P_SetMobjState(numbermobj, numbermobj->info->spawnstate+3);
}
else if (player->powers[pw_underwater] == 5*TICRATE + 1 || player->powers[pw_spacetime] == 5*TICRATE + 1)
{
numbermobj = P_SpawnMobj(player->mo->x, player->mo->y, height, MT_DROWNNUMBERS);
P_SetMobjState(numbermobj, numbermobj->info->spawnstate+2);
}
else if (player->powers[pw_underwater] == 3*TICRATE + 1 || player->powers[pw_spacetime] == 3*TICRATE + 1)
{
numbermobj = P_SpawnMobj(player->mo->x, player->mo->y, height, MT_DROWNNUMBERS);
P_SetMobjState(numbermobj, numbermobj->info->spawnstate+1);
}
else if (player->powers[pw_underwater] == 1*TICRATE + 1 || player->powers[pw_spacetime] == 1*TICRATE + 1)
{
numbermobj = P_SpawnMobj(player->mo->x, player->mo->y, height, MT_DROWNNUMBERS);
//P_SetMobjState(numbermobj, numbermobj->info->spawnstate+0);
}
// Underwater timer runs out
else if (player->powers[pw_underwater] == 1)
{
mobj_t *killer;
2016-07-05 21:09:17 -07:00
if ((netgame || multiplayer) && P_IsLocalPlayer(player))
S_ChangeMusic(mapmusname, mapmusflags, true);
2014-03-15 09:59:03 -07:00
killer = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_NULL);
killer->threshold = 42; // Special flag that it was drowning which killed you.
P_DamageMobj(player->mo, killer, killer, 10000);
}
else if (player->powers[pw_spacetime] == 1)
{
2016-07-05 21:09:17 -07:00
if ((netgame || multiplayer) && P_IsLocalPlayer(player))
S_ChangeMusic(mapmusname, mapmusflags, true);
2014-03-15 09:59:03 -07:00
P_DamageMobj(player->mo, NULL, NULL, 10000);
}
if (numbermobj)
{
P_SetTarget(&numbermobj->target, player->mo);
numbermobj->threshold = 40;
S_StartSound(player->mo, sfx_dwnind);
numbermobj->destscale = player->mo->scale;
P_SetScale(numbermobj, player->mo->scale);
}
if (!(player->mo->eflags & MFE_UNDERWATER) && player->powers[pw_underwater])
{
if (player->powers[pw_underwater] <= 12*TICRATE + 1)
P_RestoreMusic(player);
player->powers[pw_underwater] = 0;
}
if (player->powers[pw_spacetime] > 1 && !P_InSpaceSector(player->mo))
{
P_RestoreMusic(player);
player->powers[pw_spacetime] = 0;
}
// Underwater audio cues
if (P_IsLocalPlayer(player) && !player->bot)
{
2016-07-05 21:09:17 -07:00
if (player->powers[pw_underwater] == 11*TICRATE + 1
&& player == &players[consoleplayer])
{
S_StopMusic();
S_ChangeMusicInternal("drown", false);
}
2014-03-15 09:59:03 -07:00
if (player->powers[pw_underwater] == 25*TICRATE + 1)
S_StartSound(NULL, sfx_wtrdng);
else if (player->powers[pw_underwater] == 20*TICRATE + 1)
S_StartSound(NULL, sfx_wtrdng);
else if (player->powers[pw_underwater] == 15*TICRATE + 1)
S_StartSound(NULL, sfx_wtrdng);
}
if (player->exiting)
{
if (player->powers[pw_underwater] > 1)
player->powers[pw_underwater] = 0;
player->powers[pw_spacetime] = 0;
}
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
}*/
2014-03-15 09:59:03 -07:00
//
// P_CheckInvincibilityTimer
//
// Restores music from invincibility, and handles invincibility sparkles
//
static void P_CheckInvincibilityTimer(player_t *player)
{
if (!player->powers[pw_invulnerability] && !player->kartstuff[k_invincibilitytimer])
2014-08-03 20:49:33 -07:00
return;
2014-03-15 09:59:03 -07:00
//if (mariomode && !player->powers[pw_super]) // SRB2kart
2014-08-03 20:49:33 -07:00
player->mo->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1)));
/*if (leveltime % (TICRATE/7) == 0)
2014-08-03 20:49:33 -07:00
{
2016-07-05 21:09:17 -07:00
mobj_t *sparkle = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_IVSP);
2014-08-03 20:49:33 -07:00
sparkle->destscale = player->mo->scale;
P_SetScale(sparkle, player->mo->scale);
}*/
2014-03-15 09:59:03 -07:00
// Resume normal music stuff.
if (player->powers[pw_invulnerability] == 1 || player->kartstuff[k_invincibilitytimer] == 1)
2014-03-15 09:59:03 -07:00
{
if (!player->powers[pw_super])
{
//if (mariomode)
2014-03-15 09:59:03 -07:00
{
//if (player->powers[pw_shield] & SH_FIREFLOWER)
//{
// player->mo->color = SKINCOLOR_WHITE;
// G_GhostAddColor((INT32) (player - players), GHC_FIREFLOWER);
//}
//else
2014-03-15 09:59:03 -07:00
{
player->mo->color = player->skincolor;
G_GhostAddColor((INT32) (player - players), GHC_NORMAL);
2014-03-15 09:59:03 -07:00
}
}
// If you had a shield, restore its visual significance
P_SpawnShieldOrb(player);
}
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
/*if ((player->powers[pw_underwater] <= 11*TICRATE + 1)
2014-03-15 09:59:03 -07:00
&& (player->powers[pw_underwater] > 1))
return; // don't restore music if drowning music is playing
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
if (!player->powers[pw_super] || (mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC))*/
2014-03-15 09:59:03 -07:00
P_RestoreMusic(player);
}
}
//
// P_DoBubbleBreath
//
// Handles bubbles spawned by the player
//
static void P_DoBubbleBreath(player_t *player)
{
fixed_t zh;
mobj_t *bubble = NULL;
if (player->mo->eflags & MFE_VERTICALFLIP)
zh = player->mo->z + player->mo->height - FixedDiv(player->mo->height,5*(FRACUNIT/4));
else
zh = player->mo->z + FixedDiv(player->mo->height,5*(FRACUNIT/4));
2014-08-03 20:49:33 -07:00
if (!(player->mo->eflags & MFE_UNDERWATER) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL && !(player->pflags & PF_NIGHTSMODE)) || player->spectator)
2014-03-15 09:59:03 -07:00
return;
2016-07-05 21:09:17 -07:00
if (P_RandomChance(FRACUNIT/16))
2014-03-15 09:59:03 -07:00
bubble = P_SpawnMobj(player->mo->x, player->mo->y, zh, MT_SMALLBUBBLE);
2016-07-05 21:09:17 -07:00
else if (P_RandomChance(3*FRACUNIT/256))
2014-03-15 09:59:03 -07:00
bubble = P_SpawnMobj(player->mo->x, player->mo->y, zh, MT_MEDIUMBUBBLE);
if (bubble)
{
bubble->threshold = 42;
bubble->destscale = player->mo->scale;
P_SetScale(bubble, bubble->destscale);
}
2014-08-03 20:49:33 -07:00
if (player->pflags & PF_NIGHTSMODE) // NiGHTS Super doesn't spawn flight bubbles
return;
2014-03-15 09:59:03 -07:00
// Tails stirs up the water while flying in it
2019-05-01 21:39:49 -07:00
/*if (player->powers[pw_tailsfly] && (leveltime & 1) && player->charability != CA_SWIM)
2014-03-15 09:59:03 -07:00
{
fixed_t radius = (3*player->mo->radius)>>1;
angle_t fa = ((leveltime%45)*FINEANGLES/8) & FINEMASK;
fixed_t stirwaterx = FixedMul(FINECOSINE(fa),radius);
fixed_t stirwatery = FixedMul(FINESINE(fa),radius);
fixed_t stirwaterz;
if (player->mo->eflags & MFE_VERTICALFLIP)
stirwaterz = player->mo->z + player->mo->height - FixedDiv(player->mo->height,3*FRACUNIT/2);
else
stirwaterz = player->mo->z + FixedDiv(player->mo->height,3*FRACUNIT/2);
bubble = P_SpawnMobj(
player->mo->x + stirwaterx,
player->mo->y + stirwatery,
stirwaterz, MT_SMALLBUBBLE);
bubble->destscale = player->mo->scale;
P_SetScale(bubble,bubble->destscale);
bubble = P_SpawnMobj(
player->mo->x - stirwaterx,
player->mo->y - stirwatery,
stirwaterz, MT_SMALLBUBBLE);
bubble->destscale = player->mo->scale;
P_SetScale(bubble,bubble->destscale);
2019-05-01 21:39:49 -07:00
}*/
2014-03-15 09:59:03 -07:00
}
//
// P_DoPlayerHeadSigns
//
// Spawns "IT" and "GOT FLAG" signs for Tag and CTF respectively
//
static void P_DoPlayerHeadSigns(player_t *player)
{
if (G_TagGametype())
{
// If you're "IT", show a big "IT" over your head for others to see.
if (player->pflags & PF_TAGIT)
{
if (!P_IsDisplayPlayer(player)) // Don't display it on your own view.
2014-03-15 09:59:03 -07:00
{
if (!(player->mo->eflags & MFE_VERTICALFLIP))
P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height, MT_TAG);
else
P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z - mobjinfo[MT_TAG].height, MT_TAG)->eflags |= MFE_VERTICALFLIP;
}
}
}
else if (gametype == GT_CTF)
{
if (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)) // If you have the flag (duh).
{
// Spawn a got-flag message over the head of the player that
// has it (but not on your own screen if you have the flag).
if (splitscreen || player != &players[consoleplayer])
2014-03-15 09:59:03 -07:00
{
if (player->gotflag & GF_REDFLAG)
{
if (!(player->mo->eflags & MFE_VERTICALFLIP))
P_SpawnMobj(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy,
player->mo->z+P_GetPlayerHeight(player)+FixedMul(16*FRACUNIT, player->mo->scale)+player->mo->momz, MT_GOTFLAG);
else
P_SpawnMobj(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy,
player->mo->z+player->mo->height-P_GetPlayerHeight(player)-mobjinfo[MT_GOTFLAG].height-FixedMul(16*FRACUNIT, player->mo->scale)+player->mo->momz, MT_GOTFLAG)->eflags |= MFE_VERTICALFLIP;
}
if (player->gotflag & GF_BLUEFLAG)
{
if (!(player->mo->eflags & MFE_VERTICALFLIP))
P_SpawnMobj(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy,
player->mo->z+P_GetPlayerHeight(player)+FixedMul(16*FRACUNIT, player->mo->scale)+player->mo->momz, MT_GOTFLAG2);
else
P_SpawnMobj(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy,
player->mo->z+player->mo->height-P_GetPlayerHeight(player)-mobjinfo[MT_GOTFLAG2].height-FixedMul(16*FRACUNIT, player->mo->scale)+player->mo->momz, MT_GOTFLAG2)->eflags |= MFE_VERTICALFLIP;
}
}
}
}
}
//
// P_DoClimbing
//
// Handles player climbing
//
/*
static void P_DoClimbing(player_t *player) // SRB2kart - unused
2014-03-15 09:59:03 -07:00
{
2016-08-14 20:51:08 -07:00
return; // SRB2kart - don't need
2014-03-15 09:59:03 -07:00
ticcmd_t *cmd = &player->cmd;
fixed_t platx;
fixed_t platy;
subsector_t *glidesector;
//boolean climb = true; // SRB2kart - unused
2014-03-15 09:59:03 -07:00
platx = P_ReturnThrustX(player->mo, player->mo->angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
platy = P_ReturnThrustY(player->mo, player->mo->angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
glidesector = R_IsPointInSubsector(player->mo->x + platx, player->mo->y + platy);
2014-03-15 09:59:03 -07:00
if (!glidesector || glidesector->sector != player->mo->subsector->sector)
2014-03-15 09:59:03 -07:00
{
boolean floorclimb = false;
//boolean thrust = false; // SRB2kart - unused
//boolean boostup = false; // SRB2kart - unused
//boolean skyclimber = false; // SRB2kart - unused
2016-07-05 21:09:17 -07:00
fixed_t floorheight, ceilingheight; // ESLOPE
2014-03-15 09:59:03 -07:00
if (!glidesector)
floorclimb = true;
else
{
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
floorheight = glidesector->sector->f_slope ? P_GetZAt(glidesector->sector->f_slope, player->mo->x, player->mo->y)
: glidesector->sector->floorheight;
ceilingheight = glidesector->sector->c_slope ? P_GetZAt(glidesector->sector->c_slope, player->mo->x, player->mo->y)
: glidesector->sector->ceilingheight;
2016-07-05 21:09:17 -07:00
#else
floorheight = glidesector->sector->floorheight;
ceilingheight = glidesector->sector->ceilingheight;
2016-07-05 21:09:17 -07:00
#endif
if (glidesector->sector->ffloors)
2014-03-15 09:59:03 -07:00
{
ffloor_t *rover;
fixed_t topheight, bottomheight; // ESLOPE
2014-03-15 09:59:03 -07:00
for (rover = glidesector->sector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP))
continue;
floorclimb = true;
2014-03-15 09:59:03 -07:00
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight;
bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight;
2016-07-05 21:09:17 -07:00
#else
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
2016-07-05 21:09:17 -07:00
#endif
// Only supports rovers that are moving like an 'elevator', not just the top or bottom.
if (rover->master->frontsector->floorspeed && rover->master->frontsector->ceilspeed == 42)
2014-03-15 09:59:03 -07:00
{
if ((!(player->mo->eflags & MFE_VERTICALFLIP) && (bottomheight < player->mo->z+player->mo->height)
&& (topheight >= player->mo->z + FixedMul(16*FRACUNIT, player->mo->scale)))
|| ((player->mo->eflags & MFE_VERTICALFLIP) && (topheight > player->mo->z)
&& (bottomheight <= player->mo->z + player->mo->height - FixedMul(16*FRACUNIT, player->mo->scale))))
2014-03-15 09:59:03 -07:00
{
if (cmd->forwardmove != 0)
player->mo->momz += rover->master->frontsector->floorspeed;
else
{
player->mo->momz = rover->master->frontsector->floorspeed;
climb = false;
}
2014-03-15 09:59:03 -07:00
}
}
// Gravity is flipped, so the comments are, too.
if (player->mo->eflags & MFE_VERTICALFLIP)
2014-03-15 09:59:03 -07:00
{
// Trying to climb down past the bottom of the FOF
if ((topheight >= player->mo->z + player->mo->height) && ((player->mo->z + player->mo->height + player->mo->momz) >= topheight))
2014-03-15 09:59:03 -07:00
{
fixed_t bottomheight2;
ffloor_t *roverbelow;
boolean foundfof = false;
floorclimb = true;
boostup = false;
// Is there a FOF directly below this one that we can move onto?
for (roverbelow = glidesector->sector->ffloors; roverbelow; roverbelow = roverbelow->next)
{
if (!(roverbelow->flags & FF_EXISTS) || !(roverbelow->flags & FF_BLOCKPLAYER) || (roverbelow->flags & FF_BUSTUP))
continue;
2014-03-15 09:59:03 -07:00
if (roverbelow == rover)
continue;
2014-03-15 09:59:03 -07:00
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
bottomheight2 = *roverbelow->b_slope ? P_GetZAt(*roverbelow->b_slope, player->mo->x, player->mo->y) : *roverbelow->bottomheight;
2016-07-05 21:09:17 -07:00
#else
bottomheight2 = *roverbelow->bottomheight;
2016-07-05 21:09:17 -07:00
#endif
if (bottomheight2 < topheight + FixedMul(16*FRACUNIT, player->mo->scale))
foundfof = true;
}
2014-03-15 09:59:03 -07:00
if (!foundfof)
player->mo->momz = 0;
}
2014-03-15 09:59:03 -07:00
// Below the FOF
if (topheight <= player->mo->z)
{
floorclimb = false;
boostup = false;
thrust = false;
}
2014-03-15 09:59:03 -07:00
// Above the FOF
if (bottomheight > player->mo->z + player->mo->height - FixedMul(16*FRACUNIT, player->mo->scale))
{
floorclimb = false;
thrust = true;
boostup = true;
}
2014-03-15 09:59:03 -07:00
}
else
2014-03-15 09:59:03 -07:00
{
// Trying to climb down past the bottom of a FOF
if ((bottomheight <= player->mo->z) && ((player->mo->z + player->mo->momz) <= bottomheight))
2014-03-15 09:59:03 -07:00
{
fixed_t topheight2;
ffloor_t *roverbelow;
boolean foundfof = false;
floorclimb = true;
boostup = false;
// Is there a FOF directly below this one that we can move onto?
for (roverbelow = glidesector->sector->ffloors; roverbelow; roverbelow = roverbelow->next)
{
if (!(roverbelow->flags & FF_EXISTS) || !(roverbelow->flags & FF_BLOCKPLAYER) || (roverbelow->flags & FF_BUSTUP))
continue;
2014-03-15 09:59:03 -07:00
if (roverbelow == rover)
continue;
2014-03-15 09:59:03 -07:00
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
topheight2 = *roverbelow->t_slope ? P_GetZAt(*roverbelow->t_slope, player->mo->x, player->mo->y) : *roverbelow->topheight;
2016-07-05 21:09:17 -07:00
#else
topheight2 = *roverbelow->topheight;
2016-07-05 21:09:17 -07:00
#endif
if (topheight2 > bottomheight - FixedMul(16*FRACUNIT, player->mo->scale))
foundfof = true;
}
if (!foundfof)
player->mo->momz = 0;
2014-03-15 09:59:03 -07:00
}
// Below the FOF
if (bottomheight >= player->mo->z + player->mo->height)
{
floorclimb = false;
boostup = false;
thrust = false;
}
2014-03-15 09:59:03 -07:00
// Above the FOF
if (topheight < player->mo->z + FixedMul(16*FRACUNIT, player->mo->scale))
{
floorclimb = false;
thrust = true;
boostup = true;
}
2014-03-15 09:59:03 -07:00
}
if (floorclimb)
2014-03-15 09:59:03 -07:00
{
if (rover->flags & FF_CRUMBLE && !(netgame && player->spectator))
EV_StartCrumble(rover->master->frontsector, rover, (rover->flags & FF_FLOATBOB), player, rover->alpha, !(rover->flags & FF_NORETURN));
break;
2014-03-15 09:59:03 -07:00
}
}
}
// Gravity is flipped, so are comments.
if (player->mo->eflags & MFE_VERTICALFLIP)
2014-03-15 09:59:03 -07:00
{
// Trying to climb down past the upper texture area
if ((floorheight >= player->mo->z + player->mo->height) && ((player->mo->z + player->mo->height + player->mo->momz) >= floorheight))
2014-03-15 09:59:03 -07:00
{
boolean foundfof = false;
floorclimb = true;
// Is there a FOF directly below that we can move onto?
if (glidesector->sector->ffloors)
2014-03-15 09:59:03 -07:00
{
fixed_t bottomheight;
ffloor_t *rover;
for (rover = glidesector->sector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP))
continue;
2014-03-15 09:59:03 -07:00
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight;
2016-07-05 21:09:17 -07:00
#else
bottomheight = *rover->bottomheight;
2016-07-05 21:09:17 -07:00
#endif
if (bottomheight < floorheight + FixedMul(16*FRACUNIT, player->mo->scale))
{
foundfof = true;
break;
}
2014-03-15 09:59:03 -07:00
}
}
if (!foundfof)
player->mo->momz = 0;
}
2014-03-15 09:59:03 -07:00
// Reached the top of the lower texture area
if (!floorclimb && ceilingheight > player->mo->z + player->mo->height - FixedMul(16*FRACUNIT, player->mo->scale)
&& (glidesector->sector->ceilingpic == skyflatnum || floorheight < (player->mo->z - FixedMul(8*FRACUNIT, player->mo->scale))))
{
thrust = true;
boostup = true;
// Play climb-up animation here
}
2014-03-15 09:59:03 -07:00
}
else
2014-03-15 09:59:03 -07:00
{
// Trying to climb down past the upper texture area
if ((ceilingheight <= player->mo->z) && ((player->mo->z + player->mo->momz) <= ceilingheight))
2014-03-15 09:59:03 -07:00
{
boolean foundfof = false;
floorclimb = true;
2014-03-15 09:59:03 -07:00
// Is there a FOF directly below that we can move onto?
if (glidesector->sector->ffloors)
{
fixed_t topheight;
ffloor_t *rover;
for (rover = glidesector->sector->ffloors; rover; rover = rover->next)
2014-03-15 09:59:03 -07:00
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP))
continue;
#ifdef ESLOPE
topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight;
#else
topheight = *rover->topheight;
#endif
if (topheight > ceilingheight - FixedMul(16*FRACUNIT, player->mo->scale))
{
foundfof = true;
break;
}
2014-03-15 09:59:03 -07:00
}
}
if (!foundfof)
player->mo->momz = 0;
2014-03-15 09:59:03 -07:00
}
// Allow climbing from a FOF or lower texture onto the upper texture and vice versa.
if (player->mo->z > ceilingheight - FixedMul(16*FRACUNIT, player->mo->scale))
{
floorclimb = true;
thrust = false;
boostup = false;
}
2014-03-15 09:59:03 -07:00
// Reached the top of the lower texture area
if (!floorclimb && floorheight < player->mo->z + FixedMul(16*FRACUNIT, player->mo->scale)
&& (glidesector->sector->ceilingpic == skyflatnum || ceilingheight > (player->mo->z + player->mo->height + FixedMul(8*FRACUNIT, player->mo->scale))))
{
thrust = true;
boostup = true;
// Play climb-up animation here
}
2014-03-15 09:59:03 -07:00
}
// Trying to climb on the sky
if ((ceilingheight < player->mo->z) && glidesector->sector->ceilingpic == skyflatnum)
2014-03-15 09:59:03 -07:00
{
skyclimber = true;
2014-03-15 09:59:03 -07:00
}
// Climbing on the lower texture area?
if ((!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z + FixedMul(16*FRACUNIT, player->mo->scale) < floorheight)
|| ((player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z + player->mo->height <= floorheight))
2014-03-15 09:59:03 -07:00
{
floorclimb = true;
if (glidesector->sector->floorspeed)
2014-03-15 09:59:03 -07:00
{
if (cmd->forwardmove != 0)
player->mo->momz += glidesector->sector->floorspeed;
else
{
player->mo->momz = glidesector->sector->floorspeed;
climb = false;
}
2014-03-15 09:59:03 -07:00
}
}
// Climbing on the upper texture area?
else if ((!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z >= ceilingheight)
|| ((player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z + player->mo->height - FixedMul(16*FRACUNIT, player->mo->scale) > ceilingheight))
2014-03-15 09:59:03 -07:00
{
floorclimb = true;
if (glidesector->sector->ceilspeed)
2014-03-15 09:59:03 -07:00
{
if (cmd->forwardmove != 0)
player->mo->momz += glidesector->sector->ceilspeed;
else
{
player->mo->momz = glidesector->sector->ceilspeed;
climb = false;
}
2014-03-15 09:59:03 -07:00
}
}
}
if (player->lastsidehit != -1 && player->lastlinehit != -1)
{
thinker_t *think;
scroll_t *scroller;
angle_t sideangle;
2015-01-01 11:50:31 -08:00
fixed_t dx, dy;
2014-03-15 09:59:03 -07:00
for (think = thinkercap.next; think != &thinkercap; think = think->next)
{
if (think->function.acp1 != (actionf_p1)T_Scroll)
continue;
scroller = (scroll_t *)think;
if (scroller->type != sc_side)
continue;
if (scroller->affectee != player->lastsidehit)
continue;
2015-01-01 11:50:31 -08:00
if (scroller->accel)
{
dx = scroller->vdx;
dy = scroller->vdy;
}
else
{
dx = scroller->dx;
dy = scroller->dy;
}
2014-03-15 09:59:03 -07:00
if (cmd->forwardmove != 0)
{
2015-01-01 11:50:31 -08:00
player->mo->momz += dy;
2014-03-15 09:59:03 -07:00
climb = true;
}
else
{
2015-01-01 11:50:31 -08:00
player->mo->momz = dy;
2014-03-15 09:59:03 -07:00
climb = false;
}
sideangle = R_PointToAngle2(lines[player->lastlinehit].v2->x,lines[player->lastlinehit].v2->y,lines[player->lastlinehit].v1->x,lines[player->lastlinehit].v1->y);
if (cmd->sidemove != 0)
{
2015-01-01 11:50:31 -08:00
P_Thrust(player->mo, sideangle, dx);
2014-03-15 09:59:03 -07:00
climb = true;
}
else
{
2015-01-01 11:50:31 -08:00
P_InstaThrust(player->mo, sideangle, dx);
2014-03-15 09:59:03 -07:00
climb = false;
}
}
}
if (cmd->sidemove != 0 || cmd->forwardmove != 0)
climb = true;
else
climb = false;
if (player->climbing && climb && (player->mo->momx || player->mo->momy || player->mo->momz)
&& !(player->mo->state >= &states[S_PLAY_CLIMB2] && player->mo->state <= &states[S_PLAY_CLIMB5]))
P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB2);
else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state != &states[S_PLAY_CLIMB1])
P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB1);
if (!floorclimb)
{
if (boostup)
P_SetObjectMomZ(player->mo, 2*FRACUNIT, true);
if (thrust)
P_InstaThrust(player->mo, player->mo->angle, FixedMul(4*FRACUNIT, player->mo->scale)); // Lil' boost up.
player->climbing = 0;
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
}
if (skyclimber)
{
player->climbing = 0;
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
}
}
else
{
player->climbing = 0;
player->pflags |= PF_JUMPED;
2016-08-14 20:51:08 -07:00
//P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
2014-03-15 09:59:03 -07:00
}
if (cmd->sidemove != 0 || cmd->forwardmove != 0)
climb = true;
else
climb = false;
if (player->climbing && climb && (player->mo->momx || player->mo->momy || player->mo->momz)
&& !(player->mo->state >= &states[S_PLAY_CLIMB2] && player->mo->state <= &states[S_PLAY_CLIMB5]))
P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB2);
else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state != &states[S_PLAY_CLIMB1])
P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB1);
if (cmd->buttons & BT_BRAKE && !(player->pflags & PF_JUMPSTASIS))
2014-03-15 09:59:03 -07:00
{
player->climbing = 0;
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetObjectMomZ(player->mo, 4*FRACUNIT, false);
P_InstaThrust(player->mo, player->mo->angle, FixedMul(-4*FRACUNIT, player->mo->scale));
}
if (player == &players[consoleplayer])
localangle[0] = player->mo->angle;
else if (player == &players[displayplayers[1]])
localangle[1] = player->mo->angle;
else if (player == &players[displayplayers[2]])
localangle[2] = player->mo->angle;
else if (player == &players[displayplayers[3]])
localangle[3] = player->mo->angle;
2014-03-15 09:59:03 -07:00
if (player->climbing == 0)
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
2014-03-15 09:59:03 -07:00
if (player->climbing && P_IsObjectOnGround(player->mo))
{
P_ResetPlayer(player);
P_SetPlayerMobjState(player->mo, S_KART_STND1); // SRB2kart
2014-03-15 09:59:03 -07:00
}
}
2015-01-01 11:50:31 -08:00
//
// PIT_CheckSolidsTeeter
// AKA: PIT_HacksToStopPlayersTeeteringOnGargoyles
//
static mobj_t *teeterer; // the player checking for teetering
static boolean solidteeter; // saves whether the player can teeter on this or not
static fixed_t highesttop; // highest floor found so far
// reserved for standing on multiple objects
static boolean couldteeter;
static fixed_t teeterxl, teeterxh;
static fixed_t teeteryl, teeteryh;
static boolean PIT_CheckSolidsTeeter(mobj_t *thing) // SRB2kart - unused.
2015-01-01 11:50:31 -08:00
{
fixed_t blockdist;
fixed_t tiptop = FixedMul(MAXSTEPMOVE, mapobjectscale);
2015-01-01 11:50:31 -08:00
fixed_t thingtop = thing->z + thing->height;
fixed_t teeterertop = teeterer->z + teeterer->height;
if (!teeterer || !thing)
return true;
if (!(thing->flags & MF_SOLID))
return true;
if (thing->flags & MF_NOCLIP)
return true;
if (thing == teeterer)
return true;
if (thing->player && cv_tailspickup.value && gametype != GT_HIDEANDSEEK)
return true;
blockdist = teeterer->radius + thing->radius;
if (abs(thing->x - teeterer->x) >= blockdist || abs(thing->y - teeterer->y) >= blockdist)
return true; // didn't hit it
if (teeterer->eflags & MFE_VERTICALFLIP)
{
if (thingtop < teeterer->z)
return true;
if (thing->z > highesttop)
return true;
highesttop = thing->z;
if (thing->z > teeterertop + tiptop)
{
solidteeter = true;
return true;
}
}
else
{
if (thing->z > teeterertop)
return true;
if (thingtop < highesttop)
return true;
highesttop = thingtop;
if (thingtop < teeterer->z - tiptop)
{
solidteeter = true;
return true;
}
}
// are you standing on this?
if ((teeterer->eflags & MFE_VERTICALFLIP && thing->z - FixedMul(FRACUNIT, teeterer->scale) == teeterertop)
|| (!(teeterer->eflags & MFE_VERTICALFLIP) && thingtop + FixedMul(FRACUNIT, teeterer->scale) == teeterer->z))
{
fixed_t teeterdist = thing->radius - FixedMul(5*FRACUNIT, teeterer->scale);
// how far are you from the edge?
if (abs(teeterer->x - thing->x) > teeterdist || abs(teeterer->y - thing->y) > teeterdist)
{
if (couldteeter) // player is standing on another object already, see if we can stand on both and not teeter!
{
if (thing->x - teeterdist < teeterxl)
teeterxl = thing->x - teeterdist;
if (thing->x + teeterdist > teeterxh)
teeterxh = thing->x + teeterdist;
if (thing->y - teeterdist < teeteryl)
teeteryl = thing->y - teeterdist;
if (thing->y + teeterdist > teeteryh)
teeteryh = thing->y + teeterdist;
if (teeterer->x < teeterxl)
return true;
if (teeterer->x > teeterxh)
return true;
if (teeterer->y < teeteryl)
return true;
if (teeterer->y > teeteryh)
return true;
solidteeter = false; // you can stop teetering now!
couldteeter = false; // just in case...
return false;
}
else
{
// too far! don't change teeter status though
// save teeter x/y limits to bring up later
teeterxl = thing->x - teeterdist;
teeterxh = thing->x + teeterdist;
teeteryl = thing->y - teeterdist;
teeteryh = thing->y + teeterdist;
}
couldteeter = true;
return true;
}
solidteeter = false;
couldteeter = false;
return false; // you're definitely not teetering, that's the end of the matter
}
solidteeter = false;
return true; // you're not teetering but it's not neccessarily over, YET
}
*/
2015-01-01 11:50:31 -08:00
2014-03-15 09:59:03 -07:00
//
// P_DoTeeter
//
// Handles player teetering
//
/*
static void P_DoTeeter(player_t *player) // SRB2kart - unused.
2014-03-15 09:59:03 -07:00
{
2016-08-14 20:51:08 -07:00
return; // SRB2kart - don't need
2014-03-15 09:59:03 -07:00
boolean teeter = false;
boolean roverfloor; // solid 3d floors?
fixed_t floorheight, ceilingheight;
fixed_t topheight, bottomheight; // for 3d floor usage
const fixed_t tiptop = FixedMul(MAXSTEPMOVE, mapobjectscale); // Distance you have to be above the ground in order to teeter.
2014-03-15 09:59:03 -07:00
if (player->mo->standingslope && player->mo->standingslope->zdelta >= (FRACUNIT/2)) // Always teeter if the slope is too steep.
teeter = true;
else // Let's do some checks...
2014-03-15 09:59:03 -07:00
{
UINT8 i;
sector_t *sec;
fixed_t highestceilingheight = INT32_MIN;
fixed_t lowestfloorheight = INT32_MAX;
2014-03-15 09:59:03 -07:00
teeter = false;
roverfloor = false;
for (i = 0; i < 4; i++)
{
ffloor_t *rover;
#define xsign ((i & 1) ? -1 : 1) // 0 -> 1 | 1 -> -1 | 2 -> 1 | 3 -> -1
#define ysign ((i & 2) ? 1 : -1) // 0 -> 1 | 1 -> 1 | 2 -> -1 | 3 -> -1
fixed_t checkx = player->mo->x + (xsign*FixedMul(5*FRACUNIT, player->mo->scale));
fixed_t checky = player->mo->y + (ysign*FixedMul(5*FRACUNIT, player->mo->scale));
#undef xsign
#undef ysign
sec = R_PointInSubsector(checkx, checky)->sector;
2016-06-07 11:44:43 -07:00
ceilingheight = sec->ceilingheight;
floorheight = sec->floorheight;
#ifdef ESLOPE
if (sec->c_slope)
ceilingheight = P_GetZAt(sec->c_slope, checkx, checky);
if (sec->f_slope)
floorheight = P_GetZAt(sec->f_slope, checkx, checky);
#endif
highestceilingheight = (ceilingheight > highestceilingheight) ? ceilingheight : highestceilingheight;
lowestfloorheight = (floorheight < lowestfloorheight) ? floorheight : lowestfloorheight;
if (!(sec->ffloors))
2014-03-15 09:59:03 -07:00
continue; // move on to the next subsector
for (rover = sec->ffloors; rover; rover = rover->next)
2014-03-15 09:59:03 -07:00
{
if (!(rover->flags & FF_EXISTS)) continue;
#ifdef ESLOPE
topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight;
bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight;
#else
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
#endif
2014-03-15 09:59:03 -07:00
if (P_CheckSolidLava(player->mo, rover))
;
else if (!(rover->flags & FF_BLOCKPLAYER || rover->flags & FF_QUICKSAND))
continue; // intangible 3d floor
if (player->mo->eflags & MFE_VERTICALFLIP)
{
if (bottomheight > ceilingheight) // Above the ceiling
2014-03-15 09:59:03 -07:00
continue;
if (bottomheight > player->mo->z + player->mo->height + tiptop
|| (topheight < player->mo->z
&& player->mo->z + player->mo->height < ceilingheight - tiptop))
2014-03-15 09:59:03 -07:00
{
teeter = true;
roverfloor = true;
}
else
{
teeter = false;
roverfloor = true;
break;
}
}
else
{
if (topheight < floorheight) // Below the floor
2014-03-15 09:59:03 -07:00
continue;
if (topheight < player->mo->z - tiptop
|| (bottomheight > player->mo->z + player->mo->height
&& player->mo->z > floorheight + tiptop))
2014-03-15 09:59:03 -07:00
{
teeter = true;
roverfloor = true;
}
else
{
teeter = false;
roverfloor = true;
break;
}
}
}
break; // break out of loop now, we're done
}
if (player->mo->eflags & MFE_VERTICALFLIP)
{
if (!teeter && !roverfloor && (highestceilingheight > player->mo->ceilingz + tiptop))
2014-03-15 09:59:03 -07:00
teeter = true;
}
else
{
if (!teeter && !roverfloor && (lowestfloorheight < player->mo->floorz - tiptop))
2014-03-15 09:59:03 -07:00
teeter = true;
}
}
{
INT32 bx, by, xl, xh, yl, yh;
yh = (unsigned)(player->mo->y + player->mo->radius - bmaporgy)>>MAPBLOCKSHIFT;
yl = (unsigned)(player->mo->y - player->mo->radius - bmaporgy)>>MAPBLOCKSHIFT;
xh = (unsigned)(player->mo->x + player->mo->radius - bmaporgx)>>MAPBLOCKSHIFT;
xl = (unsigned)(player->mo->x - player->mo->radius - bmaporgx)>>MAPBLOCKSHIFT;
2016-07-05 21:09:17 -07:00
BMBOUNDFIX(xl, xh, yl, yh);
2015-01-01 11:50:31 -08:00
// Polyobjects
#ifdef POLYOBJECTS
validcount++;
2014-03-15 09:59:03 -07:00
for (by = yl; by <= yh; by++)
for (bx = xl; bx <= xh; bx++)
{
INT32 offset;
polymaplink_t *plink; // haleyjd 02/22/06
if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight)
continue;
offset = by*bmapwidth + bx;
// haleyjd 02/22/06: consider polyobject lines
plink = polyblocklinks[offset];
while (plink)
{
polyobj_t *po = plink->po;
if (po->validcount != validcount) // if polyobj hasn't been checked
{
sector_t *polysec;
fixed_t polytop, polybottom;
po->validcount = validcount;
if (!(po->flags & POF_SOLID))
{
plink = (polymaplink_t *)(plink->link.next);
continue;
}
if (!P_MobjInsidePolyobj(po, player->mo))
{
plink = (polymaplink_t *)(plink->link.next);
continue;
}
// We're inside it! Yess...
polysec = po->lines[0]->backsector;
if (po->flags & POF_CLIPPLANES)
{
polytop = polysec->ceilingheight;
polybottom = polysec->floorheight;
}
else
{
polytop = INT32_MAX;
polybottom = INT32_MIN;
}
if (player->mo->eflags & MFE_VERTICALFLIP)
{
if (polybottom > player->mo->ceilingz) // Above the ceiling
{
plink = (polymaplink_t *)(plink->link.next);
continue;
}
if (polybottom > player->mo->z + player->mo->height + tiptop
2016-07-05 21:09:17 -07:00
|| (polytop < player->mo->z
2015-01-01 11:50:31 -08:00
&& player->mo->z + player->mo->height < player->mo->ceilingz - tiptop))
2014-03-15 09:59:03 -07:00
teeter = true;
else
{
teeter = false;
break;
}
}
else
{
if (polytop < player->mo->floorz) // Below the floor
{
plink = (polymaplink_t *)(plink->link.next);
continue;
}
if (polytop < player->mo->z - tiptop
2016-07-05 21:09:17 -07:00
|| (polybottom > player->mo->z + player->mo->height
2015-01-01 11:50:31 -08:00
&& player->mo->z > player->mo->floorz + tiptop))
2014-03-15 09:59:03 -07:00
teeter = true;
else
{
teeter = false;
break;
}
}
}
plink = (polymaplink_t *)(plink->link.next);
}
}
#endif
2015-01-01 11:50:31 -08:00
if (teeter) // only bother with objects as a last resort if you were already teetering
{
mobj_t *oldtmthing = tmthing;
tmthing = teeterer = player->mo;
teeterxl = teeterxh = player->mo->x;
teeteryl = teeteryh = player->mo->y;
couldteeter = false;
solidteeter = teeter;
for (by = yl; by <= yh; by++)
for (bx = xl; bx <= xh; bx++)
{
highesttop = INT32_MIN;
if (!P_BlockThingsIterator(bx, by, PIT_CheckSolidsTeeter))
goto teeterdone; // we've found something that stops us teetering at all, how about we stop already
}
teeterdone:
teeter = solidteeter;
tmthing = oldtmthing; // restore old tmthing, goodness knows what the game does with this before mobj thinkers
}
}
2014-03-15 09:59:03 -07:00
if (teeter)
{
if ((player->mo->state == &states[S_PLAY_STND] || player->mo->state == &states[S_PLAY_TAP1] || player->mo->state == &states[S_PLAY_TAP2] || player->mo->state == &states[S_PLAY_SUPERSTAND]))
P_SetPlayerMobjState(player->mo, S_PLAY_TEETER1);
}
else if ((player->mo->state == &states[S_PLAY_TEETER1] || player->mo->state == &states[S_PLAY_TEETER2] || player->mo->state == &states[S_PLAY_SUPERTEETER]))
2014-03-15 09:59:03 -07:00
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
}
*/
2014-03-15 09:59:03 -07:00
//
// P_SetWeaponDelay
//
// Sets weapon delay. Properly accounts for Knux's firing rate bonus.
//
/*
static void P_SetWeaponDelay(player_t *player, INT32 delay) // SRB2kart - unused.
2014-03-15 09:59:03 -07:00
{
player->weapondelay = delay;
if (player->skin == 2) // Knuckles
{
// Multiply before dividing.
// Loss of precision can make a surprisingly large difference.
player->weapondelay *= 2;
player->weapondelay /= 3;
}
}
//
// P_DoFiring()
//
// Handles firing ring weapons and Mario fireballs.
//
static void P_DoFiring(player_t *player, ticcmd_t *cmd) // SRB2kart - unused.
2014-03-15 09:59:03 -07:00
{
INT32 i;
I_Assert(player != NULL);
I_Assert(!P_MobjWasRemoved(player->mo));
if (cmd->buttons & BT_ATTACK)
2014-03-15 09:59:03 -07:00
{
if (!(player->pflags & PF_ATTACKDOWN) && player->powers[pw_shield] & SH_FIREFLOWER && !player->climbing)
{
player->pflags |= PF_ATTACKDOWN;
P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0);
S_StartSound(player->mo, sfx_mario7);
}
else if (G_BattleGametype() && (!G_TagGametype() || player->pflags & PF_TAGIT)
2014-03-15 09:59:03 -07:00
&& !player->weapondelay && !player->climbing
&& !(player->pflags & PF_ATTACKDOWN))
{
mobj_t *mo = NULL;
player->pflags |= PF_ATTACKDOWN;
//if (cmd->buttons & BT_FIRENORMAL) // No powers, just a regular ring.
// goto firenormal; //code repetition sucks.
2014-03-15 09:59:03 -07:00
// Bounce ring
if (player->currentweapon == WEP_BOUNCE && player->powers[pw_bouncering])
2014-03-15 09:59:03 -07:00
{
if (player->health <= 1)
return;
P_SetWeaponDelay(player, TICRATE/4);
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING);
if (mo)
mo->fuse = 3*TICRATE; // Bounce Ring time
player->powers[pw_bouncering]--;
player->mo->health--;
player->health--;
}
// Rail ring
else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring])
{
if (player->health <= 1)
return;
P_SetWeaponDelay(player, (3*TICRATE)/2);
mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW);
// Rail has no unique thrown object, therefore its sound plays here.
S_StartSound(player->mo, sfx_rail1);
player->powers[pw_railring]--;
player->mo->health--;
player->health--;
}
// Automatic
else if (player->currentweapon == WEP_AUTO && player->powers[pw_automaticring])
{
if (player->health <= 1)
return;
player->pflags &= ~PF_ATTACKDOWN;
P_SetWeaponDelay(player, 2);
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNAUTOMATIC, MF2_AUTOMATIC);
player->powers[pw_automaticring]--;
player->mo->health--;
player->health--;
}
// Explosion
else if (player->currentweapon == WEP_EXPLODE && player->powers[pw_explosionring])
{
if (player->health <= 1)
return;
P_SetWeaponDelay(player, (3*TICRATE)/2);
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNEXPLOSION, MF2_EXPLOSION);
player->powers[pw_explosionring]--;
player->mo->health--;
player->health--;
}
// Grenade
else if (player->currentweapon == WEP_GRENADE && player->powers[pw_grenadering])
{
if (player->health <= 1)
return;
P_SetWeaponDelay(player, TICRATE/3);
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNGRENADE, MF2_EXPLOSION);
if (mo)
{
//P_InstaThrust(mo, player->mo->angle, FixedMul(mo->info->speed, player->mo->scale));
mo->fuse = mo->info->mass;
}
player->powers[pw_grenadering]--;
player->mo->health--;
player->health--;
}
// Scatter
// Note: Ignores MF2_RAILRING
else if (player->currentweapon == WEP_SCATTER && player->powers[pw_scatterring])
{
fixed_t oldz = player->mo->z;
angle_t shotangle = player->mo->angle;
angle_t oldaiming = player->aiming;
if (player->health <= 1)
return;
P_SetWeaponDelay(player, (2*TICRATE)/3);
// Center
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNSCATTER, MF2_SCATTER);
if (mo)
shotangle = R_PointToAngle2(player->mo->x, player->mo->y, mo->x, mo->y);
// Left
mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle-ANG2, true, MF2_SCATTER);
// Right
mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle+ANG2, true, MF2_SCATTER);
// Down
player->mo->z += FixedMul(12*FRACUNIT, player->mo->scale);
player->aiming += ANG1;
mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER);
// Up
player->mo->z -= FixedMul(24*FRACUNIT, player->mo->scale);
player->aiming -= ANG2;
mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER);
player->mo->z = oldz;
player->aiming = oldaiming;
player->powers[pw_scatterring]--;
player->mo->health--;
player->health--;
return;
}
// No powers, just a regular ring.
else
{
//firenormal:
2014-03-15 09:59:03 -07:00
// Infinity ring was selected.
// Mystic wants this ONLY to happen specifically if it's selected,
// and to not be able to get around it EITHER WAY with firenormal.
// Infinity Ring
if (player->currentweapon == 0
&& player->powers[pw_infinityring])
{
P_SetWeaponDelay(player, TICRATE/4);
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNINFINITY, 0);
player->powers[pw_infinityring]--;
}
// Red Ring
else
{
if (player->health <= 1)
return;
P_SetWeaponDelay(player, TICRATE/4);
mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, 0);
if (mo)
P_ColorTeamMissile(mo, player);
player->mo->health--;
player->health--;
}
}
if (mo)
{
if (mo->flags & MF_MISSILE && mo->flags2 & MF2_RAILRING)
{
const boolean nblockmap = !(mo->flags & MF_NOBLOCKMAP);
for (i = 0; i < 256; i++)
{
if (nblockmap)
{
P_UnsetThingPosition(mo);
mo->flags |= MF_NOBLOCKMAP;
P_SetThingPosition(mo);
}
if (i&1)
P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK);
if (P_RailThinker(mo))
break; // mobj was removed (missile hit a wall) or couldn't move
}
// Other rail sound plays at contact point.
S_StartSound(mo, sfx_rail2);
}
}
}
return;
}
// Not holding any firing buttons anymore.
// Release the grenade / whatever.
player->pflags &= ~PF_ATTACKDOWN;
}
*/
2014-03-15 09:59:03 -07:00
//
// P_DoSpinDash
//
// Player spindash handling
//
/*
static void P_DoSpinDash(player_t *player, ticcmd_t *cmd) // SRB2kart - unused.
2014-03-15 09:59:03 -07:00
{
2016-08-14 20:51:08 -07:00
return; // SRB2kart - what's a spindash?
2014-03-15 09:59:03 -07:00
if (player->pflags & PF_STASIS)
return;
2014-08-03 20:49:33 -07:00
#ifdef HAVE_BLUA
if (cmd->buttons & BT_BRAKE)
2014-08-03 20:49:33 -07:00
{
if (LUAh_SpinSpecial(player))
return;
}
#endif
2014-03-15 09:59:03 -07:00
// Spinning and Spindashing
if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_SLIDING) && !player->exiting
&& !P_PlayerInPain(player)) // subsequent revs
{
if ((cmd->buttons & BT_BRAKE) && player->speed < FixedMul(5<<FRACBITS, player->mo->scale) && !player->mo->momz && onground && !(player->pflags & PF_USEDOWN) && !(player->pflags & PF_SPINNING)
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
&& (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2)
2016-07-05 21:09:17 -07:00
#endif
)
2014-03-15 09:59:03 -07:00
{
player->mo->momx = player->cmomx;
player->mo->momy = player->cmomy;
player->pflags |= PF_STARTDASH|PF_SPINNING;
player->dashspeed = FixedMul(FRACUNIT, player->mo->scale);
player->dashtime = 0;
2016-08-14 20:51:08 -07:00
//P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
2014-03-15 09:59:03 -07:00
player->pflags |= PF_USEDOWN;
}
else if ((cmd->buttons & BT_BRAKE) && (player->pflags & PF_STARTDASH))
2014-03-15 09:59:03 -07:00
{
player->dashspeed += FixedMul(FRACUNIT, player->mo->scale);
if (!(player->dashtime++ % 5))
{
if (!player->spectator && player->dashspeed < FixedMul(player->maxdash, player->mo->scale))
S_StartSound(player->mo, sfx_spndsh); // Make the rev sound!
// Now spawn the color thok circle.
P_SpawnSpinMobj(player, player->revitem);
if (demo.recording)
G_GhostAddRev((INT32) (player - players));
2014-03-15 09:59:03 -07:00
}
}
// If not moving up or down, and travelling faster than a speed of four while not holding
// down the spin button and not spinning.
// AKA Just go into a spin on the ground, you idiot. ;)
else if ((cmd->buttons & BT_BRAKE || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20))
2016-07-05 21:09:17 -07:00
&& !player->climbing && !player->mo->momz && onground && (player->speed > FixedMul(5<<FRACBITS, player->mo->scale)
#ifdef ESLOPE
|| (player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) >= FRACUNIT/2)
2016-07-05 21:09:17 -07:00
#endif
) && !(player->pflags & PF_USEDOWN) && !(player->pflags & PF_SPINNING))
2014-03-15 09:59:03 -07:00
{
player->pflags |= PF_SPINNING;
2016-08-14 20:51:08 -07:00
//P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
2014-03-15 09:59:03 -07:00
if (!player->spectator)
S_StartSound(player->mo, sfx_spin);
player->pflags |= PF_USEDOWN;
}
}
// Rolling normally
if (onground && player->pflags & PF_SPINNING && !(player->pflags & PF_STARTDASH)
2016-07-05 21:09:17 -07:00
&& player->speed < FixedMul(5*FRACUNIT,player->mo->scale)
#ifdef ESLOPE
&& (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2)
2016-07-05 21:09:17 -07:00
#endif
)
2014-03-15 09:59:03 -07:00
{
if (GETSECSPECIAL(player->mo->subsector->sector->special, 4) == 7 || (player->mo->ceilingz - player->mo->floorz < P_GetPlayerHeight(player)))
P_InstaThrust(player->mo, player->mo->angle, FixedMul(10*FRACUNIT, player->mo->scale));
else
{
player->skidtime = 0;
player->pflags &= ~PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_KART_STND1);
2014-03-15 09:59:03 -07:00
player->mo->momx = player->cmomx;
player->mo->momy = player->cmomy;
}
}
// Catapult the player from a spindash rev!
if (onground && !(player->pflags & PF_USEDOWN) && player->dashspeed && (player->pflags & PF_STARTDASH) && (player->pflags & PF_SPINNING))
{
if (player->powers[pw_ingoop])
player->dashspeed = 0;
player->pflags &= ~PF_STARTDASH;
if (!((gametype == GT_RACE || gametype == GT_COMPETITION) && leveltime < 4*TICRATE))
{
P_InstaThrust(player->mo, player->mo->angle, player->dashspeed); // catapult forward ho!!
if (!player->spectator)
S_StartSound(player->mo, sfx_zoom);
}
player->dashspeed = 0;
}
2016-08-14 20:51:08 -07:00
//if (onground && (player->pflags & PF_SPINNING) && !(player->panim == PA_ROLL))
// P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
2014-03-15 09:59:03 -07:00
}
*/
2014-03-15 09:59:03 -07:00
//
// P_DoJumpShield
//
// Jump Shield Activation
//
2014-08-03 20:49:33 -07:00
void P_DoJumpShield(player_t *player)
2014-03-15 09:59:03 -07:00
{
if (player->pflags & PF_THOKKED)
return;
player->pflags &= ~PF_JUMPED;
//P_DoJump(player, false);
2014-03-15 09:59:03 -07:00
player->pflags &= ~PF_JUMPED;
player->secondjump = 0;
player->jumping = 0;
player->pflags |= PF_THOKKED;
player->pflags &= ~PF_SPINNING;
2016-08-14 20:51:08 -07:00
//P_SetPlayerMobjState(player->mo, S_PLAY_FALL1);
2014-03-15 09:59:03 -07:00
S_StartSound(player->mo, sfx_wdjump);
}
//
// P_Telekinesis
//
// Morph's fancy stuff-moving character ability
// +ve thrust pushes away, -ve thrust pulls in
//
void P_Telekinesis(player_t *player, fixed_t thrust, fixed_t range)
2014-03-15 09:59:03 -07:00
{
thinker_t *th;
mobj_t *mo2;
fixed_t dist = 0;
angle_t an;
if (player->powers[pw_super]) // increase range when super
range *= 2;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (mo2 == player->mo)
continue;
if (!((mo2->flags & MF_SHOOTABLE && mo2->flags & MF_ENEMY) || mo2->type == MT_EGGGUARD || mo2->player))
continue;
dist = P_AproxDistance(P_AproxDistance(player->mo->x-mo2->x, player->mo->y-mo2->y), player->mo->z-mo2->z);
if (range < dist)
continue;
if (!P_CheckSight(player->mo, mo2))
continue; // if your psychic powers can't "see" it don't bother
an = R_PointToAngle2(player->mo->x, player->mo->y, mo2->x, mo2->y);
if (mo2->health > 0)
{
P_Thrust(mo2, an, thrust);
if (mo2->type == MT_GOLDBUZZ || mo2->type == MT_REDBUZZ)
mo2->tics += 8;
}
}
//P_SpawnThokMobj(player);
2014-03-15 09:59:03 -07:00
player->pflags |= PF_THOKKED;
}
2016-07-05 21:09:17 -07:00
boolean P_AnalogMove(player_t *player)
2014-08-03 20:49:33 -07:00
{
2014-11-11 16:55:07 -08:00
return player->pflags & PF_ANALOGMODE;
2014-03-15 09:59:03 -07:00
}
//
// P_GetPlayerControlDirection
//
// Determines if the player is pressing in the direction they are moving
//
2014-08-26 20:56:30 -07:00
// 0 = no controls pressed/no movement
2014-03-15 09:59:03 -07:00
// 1 = pressing in the direction of movement
// 2 = pressing in the opposite direction of movement
//
/*INT32 P_GetPlayerControlDirection(player_t *player)
2014-03-15 09:59:03 -07:00
{
ticcmd_t *cmd = &player->cmd;
angle_t controllerdirection, controlplayerdirection;
camera_t *thiscam;
2014-08-03 20:49:33 -07:00
angle_t dangle;
fixed_t tempx = 0, tempy = 0;
angle_t tempangle, origtempangle;
2014-03-15 09:59:03 -07:00
if (splitscreen > 2 && player == &players[displayplayers[3]])
thiscam = &camera[3];
else if (splitscreen > 1 && player == &players[displayplayers[2]])
thiscam = &camera[2];
else if (splitscreen && player == &players[displayplayers[1]])
thiscam = &camera[1];
2014-03-15 09:59:03 -07:00
else
thiscam = &camera[0];
2014-03-15 09:59:03 -07:00
2014-08-03 20:49:33 -07:00
if (!cmd->forwardmove && !cmd->sidemove)
return 0;
2014-03-15 09:59:03 -07:00
2014-08-26 20:56:30 -07:00
if (!player->mo->momx && !player->mo->momy)
return 0;
if (twodlevel || player->mo->flags2 & MF2_TWOD)
{
if (!cmd->sidemove)
return 0;
if (!player->mo->momx)
return 0;
origtempangle = tempangle = 0; // relative to the axis rather than the player!
controlplayerdirection = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
}
else if (P_AnalogMove(player) && thiscam->chase)
2014-08-03 20:49:33 -07:00
{
if (player->awayviewtics)
origtempangle = tempangle = player->awayviewmobj->angle;
else
origtempangle = tempangle = thiscam->angle;
2014-03-15 09:59:03 -07:00
controlplayerdirection = player->mo->angle;
}
else
{
2014-08-03 20:49:33 -07:00
origtempangle = tempangle = player->mo->angle;
controlplayerdirection = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
2014-03-15 09:59:03 -07:00
}
2014-08-03 20:49:33 -07:00
// Calculate the angle at which the controls are pointing
// to figure out the proper mforward and mbackward.
tempangle >>= ANGLETOFINESHIFT;
2014-08-26 20:56:30 -07:00
if (!(twodlevel || player->mo->flags2 & MF2_TWOD)) // in 2d mode, sidemove is treated as the forwards/backwards direction
{
tempx += FixedMul(cmd->forwardmove*FRACUNIT,FINECOSINE(tempangle));
tempy += FixedMul(cmd->forwardmove*FRACUNIT,FINESINE(tempangle));
2014-03-15 09:59:03 -07:00
2014-08-26 20:56:30 -07:00
tempangle = origtempangle-ANGLE_90;
tempangle >>= ANGLETOFINESHIFT;
}
2014-08-03 20:49:33 -07:00
tempx += FixedMul(cmd->sidemove*FRACUNIT,FINECOSINE(tempangle));
tempy += FixedMul(cmd->sidemove*FRACUNIT,FINESINE(tempangle));
2014-03-15 09:59:03 -07:00
2014-08-03 20:49:33 -07:00
controllerdirection = R_PointToAngle2(0, 0, tempx, tempy);
2014-03-15 09:59:03 -07:00
2014-08-03 20:49:33 -07:00
dangle = controllerdirection - controlplayerdirection;
if (dangle > ANGLE_180) //flip to keep to one side
dangle = InvAngle(dangle);
if (dangle > ANGLE_90)
return 2; // Controls pointing backwards from player
else
return 1; // Controls pointing in player's general direction
2014-03-15 09:59:03 -07:00
}
// Control scheme for 2d levels.
static void P_2dMovement(player_t *player)
2014-03-15 09:59:03 -07:00
{
ticcmd_t *cmd;
INT32 topspeed, acceleration, thrustfactor;
fixed_t movepushforward = 0;
angle_t movepushangle = 0;
fixed_t normalspd = FixedMul(player->normalspeed, player->mo->scale);
cmd = &player->cmd;
if (player->exiting || player->pflags & PF_STASIS)
{
cmd->forwardmove = cmd->sidemove = 0;
2014-03-24 19:17:59 -07:00
if (player->pflags & PF_GLIDING)
{
if (!player->skidtime)
player->pflags &= ~PF_GLIDING;
else if (player->exiting)
{
player->pflags &= ~PF_GLIDING;
2016-08-14 20:51:08 -07:00
P_SetPlayerMobjState(player->mo, S_KART_WALK1); // SRB2kart - was S_PLAY_RUN1
2014-03-24 19:17:59 -07:00
player->skidtime = 0;
}
}
2014-03-15 09:59:03 -07:00
if (player->pflags & PF_SPINNING && !player->exiting)
{
player->pflags &= ~PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_KART_STND1); // SRB2kart - was S_PLAY_STND
2014-03-15 09:59:03 -07:00
}
}
// cmomx/cmomy stands for the conveyor belt speed.
if (player->onconveyor == 2) // Wind/Current
{
//if (player->mo->z > player->mo->watertop || player->mo->z + player->mo->height < player->mo->waterbottom)
if (!(player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
player->cmomx = player->cmomy = 0;
}
else if (player->onconveyor == 4 && !P_IsObjectOnGround(player->mo)) // Actual conveyor belt
player->cmomx = player->cmomy = 0;
2014-11-11 16:55:07 -08:00
else if (player->onconveyor != 2 && player->onconveyor != 4
#ifdef POLYOBJECTS
&& player->onconveyor != 1
#endif
)
2014-03-15 09:59:03 -07:00
player->cmomx = player->cmomy = 0;
player->rmomx = player->mo->momx - player->cmomx;
player->rmomy = player->mo->momy - player->cmomy;
// Calculates player's speed based on absolute-value-of-a-number formula
player->speed = abs(player->rmomx);
if (player->pflags & PF_GLIDING)
{
// Angle fix.
if (player->mo->angle < ANGLE_180 && player->mo->angle > ANGLE_90)
player->mo->angle = ANGLE_180;
else if (player->mo->angle < ANGLE_90 && player->mo->angle > 0)
player->mo->angle = 0;
if (cmd->sidemove > 0 && player->mo->angle != 0 && player->mo->angle >= ANGLE_180)
player->mo->angle += 1280<<FRACBITS;
else if (cmd->sidemove < 0 && player->mo->angle != ANGLE_180 && (player->mo->angle > ANGLE_180 || player->mo->angle == 0))
player->mo->angle -= 1280<<FRACBITS;
else if (cmd->sidemove == 0)
{
if (player->mo->angle >= ANGLE_270)
player->mo->angle += 1280<<FRACBITS;
else if (player->mo->angle < ANGLE_270 && player->mo->angle > ANGLE_180)
player->mo->angle -= 1280<<FRACBITS;
}
}
else if (cmd->sidemove && !(player->climbing) && !P_PlayerInPain(player))
{
if (cmd->sidemove > 0)
player->mo->angle = 0;
else if (cmd->sidemove < 0)
player->mo->angle = ANGLE_180;
}
if (player == &players[consoleplayer])
localangle[0] = player->mo->angle;
else if (player == &players[displayplayers[1]])
localangle[1] = player->mo->angle;
else if (player == &players[displayplayers[2]])
localangle[2] = player->mo->angle;
else if (player == &players[displayplayers[3]])
localangle[3] = player->mo->angle;
2014-03-15 09:59:03 -07:00
if (player->pflags & PF_GLIDING)
movepushangle = player->mo->angle;
else
{
if (cmd->sidemove > 0)
movepushangle = 0;
else if (cmd->sidemove < 0)
movepushangle = ANGLE_180;
else
movepushangle = player->mo->angle;
}
// Do not let the player control movement if not onground.
onground = P_IsObjectOnGround(player->mo);
player->aiming = cmd->aiming<<FRACBITS;
// Set the player speeds.
if (maptol & TOL_2D)
normalspd = FixedMul(normalspd, 2*FRACUNIT/3);
if (player->powers[pw_super] || player->powers[pw_sneakers])
{
thrustfactor = player->thrustfactor*2;
acceleration = player->accelstart/2 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration/2;
if (player->powers[pw_tailsfly])
topspeed = normalspd;
else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER) && !(player->pflags & PF_SLIDING))
{
topspeed = normalspd;
acceleration = 2*acceleration/3;
}
else
topspeed = normalspd * 2;
}
else
{
thrustfactor = player->thrustfactor;
acceleration = player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration;
if (player->powers[pw_tailsfly])
topspeed = normalspd/2;
else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER) && !(player->pflags & PF_SLIDING))
{
topspeed = normalspd/2;
acceleration = 2*acceleration/3;
}
else
topspeed = normalspd;
}
//////////////////////////////////////
if (player->climbing)
{
if (cmd->forwardmove != 0)
P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT,10*FRACUNIT), false);
if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
player->mo->momz *= 2;
player->mo->momx = 0;
}
2014-08-03 20:49:33 -07:00
else if (cmd->sidemove != 0 && !(player->pflags & PF_GLIDING || player->exiting
2014-03-15 09:59:03 -07:00
|| (P_PlayerInPain(player) && !onground)))
{
movepushforward = abs(cmd->sidemove) * (thrustfactor * acceleration);
// allow very small movement while in air for gameplay
if (!onground)
movepushforward >>= 1; // Proper air movement
// Allow a bit of movement while spinning
if (player->pflags & PF_SPINNING)
{
if (!(player->pflags & PF_STARTDASH))
movepushforward = movepushforward/48;
else
movepushforward = 0;
}
movepushforward = FixedMul(movepushforward, player->mo->scale);
if (player->rmomx < topspeed && cmd->sidemove > 0) // Sonic's Speed
P_Thrust(player->mo, movepushangle, movepushforward);
else if (player->rmomx > -topspeed && cmd->sidemove < 0)
P_Thrust(player->mo, movepushangle, movepushforward);
}
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
}*/
2014-03-15 09:59:03 -07:00
//#define OLD_MOVEMENT_CODE 1
static void P_3dMovement(player_t *player)
{
ticcmd_t *cmd;
angle_t movepushangle, movepushsideangle; // Analog
//INT32 topspeed, acceleration, thrustfactor;
2014-03-15 09:59:03 -07:00
fixed_t movepushforward = 0, movepushside = 0;
angle_t dangle; // replaces old quadrants bits
//boolean dangleflip = false; // SRB2kart - toaster
//fixed_t normalspd = FixedMul(player->normalspeed, player->mo->scale);
2014-03-15 09:59:03 -07:00
boolean analogmove = false;
fixed_t oldMagnitude, newMagnitude;
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
vector3_t totalthrust;
totalthrust.x = totalthrust.y = 0; // I forget if this is needed
totalthrust.z = FRACUNIT*P_MobjFlip(player->mo)/3; // A bit of extra push-back on slopes
#endif // ESLOPE
2014-03-15 09:59:03 -07:00
// Get the old momentum; this will be needed at the end of the function! -SH
2014-11-11 16:55:07 -08:00
oldMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0);
2014-03-15 09:59:03 -07:00
2014-11-11 16:55:07 -08:00
analogmove = P_AnalogMove(player);
2014-03-15 09:59:03 -07:00
cmd = &player->cmd;
2018-07-21 21:31:02 -07:00
if ((player->exiting || mapreset) || player->pflags & PF_STASIS || player->kartstuff[k_spinouttimer]) // pw_introcam?
2014-03-15 09:59:03 -07:00
{
cmd->forwardmove = cmd->sidemove = 0;
if (player->kartstuff[k_sneakertimer])
cmd->forwardmove = 50;
2014-03-15 09:59:03 -07:00
}
if (!(player->pflags & PF_FORCESTRAFE) && !player->kartstuff[k_pogospring])
cmd->sidemove = 0;
2014-03-15 09:59:03 -07:00
if (analogmove)
{
2014-11-11 16:55:07 -08:00
movepushangle = (cmd->angleturn<<16 /* not FRACBITS */);
2014-03-15 09:59:03 -07:00
}
else
{
2017-03-06 19:00:10 -08:00
if (player->kartstuff[k_drift] != 0)
movepushangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift];
else if (player->kartstuff[k_spinouttimer] || player->kartstuff[k_wipeoutslow]) // if spun out, use the boost angle
movepushangle = (angle_t)player->kartstuff[k_boostangle];
2016-08-21 14:55:35 -07:00
else
movepushangle = player->mo->angle;
2014-03-15 09:59:03 -07:00
}
2014-11-11 16:55:07 -08:00
movepushsideangle = movepushangle-ANGLE_90;
2014-03-15 09:59:03 -07:00
// cmomx/cmomy stands for the conveyor belt speed.
if (player->onconveyor == 2) // Wind/Current
{
//if (player->mo->z > player->mo->watertop || player->mo->z + player->mo->height < player->mo->waterbottom)
if (!(player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
player->cmomx = player->cmomy = 0;
}
else if (player->onconveyor == 4 && !P_IsObjectOnGround(player->mo)) // Actual conveyor belt
player->cmomx = player->cmomy = 0;
2014-11-11 16:55:07 -08:00
else if (player->onconveyor != 2 && player->onconveyor != 4
#ifdef POLYOBJECTS
&& player->onconveyor != 1
#endif
)
2014-03-15 09:59:03 -07:00
player->cmomx = player->cmomy = 0;
player->rmomx = player->mo->momx - player->cmomx;
player->rmomy = player->mo->momy - player->cmomy;
// Calculates player's speed based on distance-of-a-line formula
player->speed = R_PointToDist2(0, 0, player->rmomx, player->rmomy);
2014-03-15 09:59:03 -07:00
// Monster Iestyn - 04-11-13
// Quadrants are stupid, excessive and broken, let's do this a much simpler way!
// Get delta angle from rmom angle and player angle first
dangle = R_PointToAngle2(0,0, player->rmomx, player->rmomy) - player->mo->angle;
if (dangle > ANGLE_180) //flip to keep to one side
{
2014-03-15 09:59:03 -07:00
dangle = InvAngle(dangle);
//dangleflip = true;
}
2014-03-15 09:59:03 -07:00
// anything else will leave both at 0, so no need to do anything else
//{ SRB2kart 220217 - Toaster Code for misplaced thrust
/*
if (!player->kartstuff[k_drift]) // Not Drifting
{
angle_t difference = dangle/2;
boolean reverse = (dangle >= ANGLE_90);
if (dangleflip)
difference = InvAngle(difference);
if (reverse)
difference += ANGLE_180;
P_InstaThrust(player->mo, player->mo->angle + difference, player->speed);
}
*/
//}
2014-03-15 09:59:03 -07:00
// When sliding, don't allow forward/back
if (player->pflags & PF_SLIDING)
cmd->forwardmove = 0;
// Do not let the player control movement if not onground.
// SRB2Kart: pogo spring and speed bumps are supposed to control like you're on the ground
2018-06-07 16:39:45 -07:00
onground = (P_IsObjectOnGround(player->mo) || (player->kartstuff[k_pogospring]));
2014-03-15 09:59:03 -07:00
player->aiming = cmd->aiming<<FRACBITS;
// Forward movement
2018-07-21 21:31:02 -07:00
if (!((player->exiting || mapreset) || (P_PlayerInPain(player) && !onground)))
2014-03-15 09:59:03 -07:00
{
//movepushforward = cmd->forwardmove * (thrustfactor * acceleration);
movepushforward = K_3dKartMovement(player, onground, cmd->forwardmove);
2014-03-15 09:59:03 -07:00
// allow very small movement while in air for gameplay
if (!onground)
movepushforward >>= 2; // proper air movement
// don't need to account for scale here with kart accel code
//movepushforward = FixedMul(movepushforward, player->mo->scale);
if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration...
movepushforward = FixedMul(movepushforward, player->mo->movefactor);
if (cmd->buttons & BT_BRAKE && !cmd->forwardmove) // SRB2kart - braking isn't instant
movepushforward /= 64;
if (cmd->forwardmove > 0)
player->kartstuff[k_brakestop] = 0;
else if (player->kartstuff[k_brakestop] < 6) // Don't start reversing with brakes until you've made a stop first
{
if (player->speed < 8*FRACUNIT)
player->kartstuff[k_brakestop]++;
movepushforward = 0;
}
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
totalthrust.x += P_ReturnThrustX(player->mo, movepushangle, movepushforward);
totalthrust.y += P_ReturnThrustY(player->mo, movepushangle, movepushforward);
2014-03-15 09:59:03 -07:00
#else
P_Thrust(player->mo, movepushangle, movepushforward);
#endif
}
2017-12-12 16:58:44 -08:00
else if (!(player->kartstuff[k_spinouttimer]))
{
K_MomentumToFacing(player);
}
2016-08-21 14:55:35 -07:00
// Sideways movement
2018-07-21 21:31:02 -07:00
if (cmd->sidemove != 0 && !((player->exiting || mapreset) || player->kartstuff[k_spinouttimer]))
2014-03-15 09:59:03 -07:00
{
if (cmd->sidemove > 0)
movepushside = (cmd->sidemove * FRACUNIT/128) + FixedDiv(player->speed, K_GetKartSpeed(player, true));
else
movepushside = (cmd->sidemove * FRACUNIT/128) - FixedDiv(player->speed, K_GetKartSpeed(player, true));
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
totalthrust.x += P_ReturnThrustX(player->mo, movepushsideangle, movepushside);
totalthrust.y += P_ReturnThrustY(player->mo, movepushsideangle, movepushside);
2014-08-03 20:49:33 -07:00
#else
P_Thrust(player->mo, movepushsideangle, movepushside);
2014-03-15 09:59:03 -07:00
#endif
}
2016-07-05 21:09:17 -07:00
#ifdef ESLOPE
if ((totalthrust.x || totalthrust.y)
&& player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) > FRACUNIT/2) {
2016-07-05 21:09:17 -07:00
// Factor thrust to slope, but only for the part pushing up it!
// The rest is unaffected.
angle_t thrustangle = R_PointToAngle2(0, 0, totalthrust.x, totalthrust.y)-player->mo->standingslope->xydirection;
if (player->mo->standingslope->zdelta < 0) { // Direction goes down, so thrustangle needs to face toward
if (thrustangle < ANGLE_90 || thrustangle > ANGLE_270) {
P_QuantizeMomentumToSlope(&totalthrust, player->mo->standingslope);
}
} else { // Direction goes up, so thrustangle needs to face away
if (thrustangle > ANGLE_90 && thrustangle < ANGLE_270) {
P_QuantizeMomentumToSlope(&totalthrust, player->mo->standingslope);
}
}
}
player->mo->momx += totalthrust.x;
player->mo->momy += totalthrust.y;
#endif
2014-03-15 09:59:03 -07:00
// Time to ask three questions:
// 1) Are we over topspeed?
// 2) If "yes" to 1, were we moving over topspeed to begin with?
// 3) If "yes" to 2, are we now going faster?
// If "yes" to 3, normalize to our initial momentum; this will allow thoks to stay as fast as they normally are.
// If "no" to 3, ignore it; the player might be going too fast, but they're slowing down, so let them.
// If "no" to 2, normalize to topspeed, so we can't suddenly run faster than it of our own accord.
// If "no" to 1, we're not reaching any limits yet, so ignore this entirely!
// -Shadow Hog
2014-11-11 16:55:07 -08:00
newMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0);
if (newMagnitude > K_GetKartSpeed(player, true)) //topspeed)
2014-03-15 09:59:03 -07:00
{
2014-11-11 16:55:07 -08:00
fixed_t tempmomx, tempmomy;
if (oldMagnitude > K_GetKartSpeed(player, true) && onground) // SRB2Kart: onground check for air speed cap
2014-03-15 09:59:03 -07:00
{
if (newMagnitude > oldMagnitude)
{
2014-11-11 16:55:07 -08:00
tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), oldMagnitude);
tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), oldMagnitude);
player->mo->momx = tempmomx + player->cmomx;
player->mo->momy = tempmomy + player->cmomy;
2014-03-15 09:59:03 -07:00
}
// else do nothing
}
else
{
tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), K_GetKartSpeed(player, true)); //topspeed)
tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), K_GetKartSpeed(player, true)); //topspeed)
2014-11-11 16:55:07 -08:00
player->mo->momx = tempmomx + player->cmomx;
player->mo->momy = tempmomy + player->cmomy;
2014-03-15 09:59:03 -07:00
}
}
}
//
// P_SpectatorMovement
//
// Control for spectators in multiplayer
//
static void P_SpectatorMovement(player_t *player)
{
ticcmd_t *cmd = &player->cmd;
player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
ticruned++;
if (!(cmd->angleturn & TICCMD_RECEIVED))
ticmiss++;
if (player->mo->z > player->mo->ceilingz - player->mo->height)
player->mo->z = player->mo->ceilingz - player->mo->height;
if (player->mo->z < player->mo->floorz)
player->mo->z = player->mo->floorz;
if (cmd->buttons & BT_ACCELERATE)
player->mo->z += 32*mapobjectscale;
else if (cmd->buttons & BT_BRAKE)
player->mo->z -= 32*mapobjectscale;
2014-03-15 09:59:03 -07:00
// Aiming needed for SEENAMES, etc.
// We may not need to fire as a spectator, but this is still handy!
player->aiming = cmd->aiming<<FRACBITS;
player->mo->momx = player->mo->momy = player->mo->momz = 0;
if (cmd->forwardmove != 0)
{
P_Thrust(player->mo, player->mo->angle, cmd->forwardmove*mapobjectscale);
2014-03-15 09:59:03 -07:00
// Quake-style flying spectators :D
player->mo->momz += FixedMul(cmd->forwardmove*mapobjectscale, AIMINGTOSLOPE(player->aiming));
2014-03-15 09:59:03 -07:00
}
/*if (cmd->sidemove != 0) -- was disabled in practice anyways, since sidemove was suppressed
2014-03-15 09:59:03 -07:00
{
P_Thrust(player->mo, player->mo->angle-ANGLE_90, cmd->sidemove*mapobjectscale);
}*/
2014-03-15 09:59:03 -07:00
}
//
// P_ShootLine
//
// Fun and fancy
// graphical indicator
// for building/debugging
// NiGHTS levels!
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
/*static void P_ShootLine(mobj_t *source, mobj_t *dest, fixed_t height)
2014-03-15 09:59:03 -07:00
{
mobj_t *mo;
INT32 i;
fixed_t temp;
INT32 speed, seesound;
temp = dest->z;
dest->z = height;
seesound = mobjinfo[MT_REDRING].seesound;
speed = mobjinfo[MT_REDRING].speed;
mobjinfo[MT_REDRING].seesound = sfx_None;
mobjinfo[MT_REDRING].speed = 20*FRACUNIT;
mo = P_SpawnXYZMissile(source, dest, MT_REDRING, source->x, source->y, height);
dest->z = temp;
if (mo)
{
mo->flags2 |= MF2_RAILRING;
mo->flags2 |= MF2_DONTDRAW;
mo->flags |= MF_NOCLIPHEIGHT;
mo->flags |= MF_NOCLIP;
mo->flags &= ~MF_MISSILE;
mo->fuse = 3;
}
for (i = 0; i < 32; i++)
{
if (mo)
{
if (!(mo->flags & MF_NOBLOCKMAP))
{
P_UnsetThingPosition(mo);
mo->flags |= MF_NOBLOCKMAP;
P_SetThingPosition(mo);
}
if (i&1)
P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK);
P_UnsetThingPosition(mo);
mo->x += mo->momx;
mo->y += mo->momy;
mo->z += mo->momz;
P_SetThingPosition(mo);
}
else
{
mobjinfo[MT_REDRING].seesound = seesound;
mobjinfo[MT_REDRING].speed = speed;
return;
}
}
mobjinfo[MT_REDRING].seesound = seesound;
mobjinfo[MT_REDRING].speed = speed;
}
#define MAXDRILLSPEED 14000
#define MAXNORMALSPEED 6250 //6000
static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t radius)
{
if (player->pflags & PF_TRANSFERTOCLOSEST)
{
const angle_t fa = R_PointToAngle2(player->axis1->x, player->axis1->y, player->axis2->x, player->axis2->y);
P_InstaThrust(player->mo, fa, xspeed/10);
}
else
{
const angle_t fa = player->angle_pos>>ANGLETOFINESHIFT;
2016-07-05 21:09:17 -07:00
const angle_t faold = player->old_angle_pos>>ANGLETOFINESHIFT;
player->mo->momx = FixedMul(FINECOSINE(fa),radius) - FixedMul(FINECOSINE(faold),radius);
player->mo->momy = FixedMul(FINESINE(fa),radius) - FixedMul(FINESINE(faold),radius);
2014-03-15 09:59:03 -07:00
}
2014-08-03 20:49:33 -07:00
if (player->exiting)
return;
2014-08-05 16:59:40 -07:00
// You're welcome, Rob. (Now with slightly less horrendous hacking -Red
player->mo->tracer->flags &= ~MF_NOCLIP;
player->mo->tracer->z = player->mo->z;
if (!P_TryMove(player->mo->tracer, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, true)) {
player->mo->tracer->flags |= MF_NOCLIP;
2014-08-03 20:49:33 -07:00
return;
}
2014-08-05 16:59:40 -07:00
player->mo->tracer->flags |= MF_NOCLIP;
2014-03-15 09:59:03 -07:00
{
const INT32 sequence = player->mo->target->threshold;
mobj_t *transfer1 = NULL;
mobj_t *transfer2 = NULL;
mobj_t *axis;
mobj_t *mo2;
thinker_t *th;
line_t transfer1line;
line_t transfer2line;
boolean transfer1last = false;
boolean transfer2last = false;
vertex_t vertices[4];
fixed_t truexspeed = xspeed*(!(player->pflags & PF_TRANSFERTOCLOSEST) && player->mo->target->flags2 & MF2_AMBUSH ? -1 : 1);
2014-03-15 09:59:03 -07:00
// Find next waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
continue;
mo2 = (mobj_t *)th;
// Axis things are only at beginning of list.
if (!(mo2->flags2 & MF2_AXIS))
break;
if ((mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE)
&& mo2->threshold == sequence)
{
if (player->pflags & PF_TRANSFERTOCLOSEST)
{
if (mo2->health == player->axis1->health)
transfer1 = mo2;
else if (mo2->health == player->axis2->health)
transfer2 = mo2;
}
else
{
if (mo2->health == player->mo->target->health)
transfer1 = mo2;
else if (mo2->health == player->mo->target->health + 1)
transfer2 = mo2;
}
}
}
// It might be possible that one wasn't found.
// Is it because we're at the end of the track?
// Look for a wrapper point.
if (!transfer1)
{
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
continue;
mo2 = (mobj_t *)th;
// Axis things are only at beginning of list.
if (!(mo2->flags2 & MF2_AXIS))
break;
if (mo2->threshold == sequence && (mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE))
{
if (!transfer1)
{
transfer1 = mo2;
transfer1last = true;
}
else if (mo2->health > transfer1->health)
{
transfer1 = mo2;
transfer1last = true;
}
}
}
}
if (!transfer2)
{
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
continue;
mo2 = (mobj_t *)th;
// Axis things are only at beginning of list.
if (!(mo2->flags2 & MF2_AXIS))
break;
if (mo2->threshold == sequence && (mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE))
{
if (!transfer2)
{
transfer2 = mo2;
transfer2last = true;
}
else if (mo2->health > transfer2->health)
{
transfer2 = mo2;
transfer2last = true;
}
}
}
}
if (!(transfer1 && transfer2)) // We can't continue...
I_Error("Mare does not form a complete circuit!\n");
transfer1line.v1 = &vertices[0];
transfer1line.v2 = &vertices[1];
transfer2line.v1 = &vertices[2];
transfer2line.v2 = &vertices[3];
if (cv_debug && (leveltime % TICRATE == 0))
{
CONS_Debug(DBG_NIGHTS, "Transfer1 : %d\n", transfer1->health);
CONS_Debug(DBG_NIGHTS, "Transfer2 : %d\n", transfer2->health);
}
2014-08-03 20:49:33 -07:00
//CONS_Debug(DBG_NIGHTS, "Xspeed : %d", truexspeed);
2014-03-15 09:59:03 -07:00
//CONS_Debug(DBG_NIGHTS, "T1 is at %d, %d\n", transfer1->x>>FRACBITS, transfer1->y>>FRACBITS);
//CONS_Debug(DBG_NIGHTS, "T2 is at %d, %d\n", transfer2->x>>FRACBITS, transfer2->y>>FRACBITS);
//CONS_Debug(DBG_NIGHTS, "Distance from T1: %d\n", P_AproxDistance(transfer1->x - player->mo->x, transfer1->y - player->mo->y)>>FRACBITS);
//CONS_Debug(DBG_NIGHTS, "Distance from T2: %d\n", P_AproxDistance(transfer2->x - player->mo->x, transfer2->y - player->mo->y)>>FRACBITS);
// Transfer1 is closer to the player than transfer2
if (P_AproxDistance(transfer1->x - player->mo->x, transfer1->y - player->mo->y)>>FRACBITS
< P_AproxDistance(transfer2->x - player->mo->x, transfer2->y - player->mo->y)>>FRACBITS)
{
2014-08-03 20:49:33 -07:00
//CONS_Debug(DBG_NIGHTS, " must be < 0 to transfer\n");
2014-03-15 09:59:03 -07:00
if (transfer1->type == MT_AXISTRANSFERLINE)
{
if (transfer1last)
axis = P_FindAxis(transfer1->threshold, transfer1->health-2);
else if (player->pflags & PF_TRANSFERTOCLOSEST)
axis = P_FindAxis(transfer1->threshold, transfer1->health-1);
else
axis = P_FindAxis(transfer1->threshold, transfer1->health);
if (!axis)
{
CONS_Debug(DBG_NIGHTS, "Unable to find an axis - error code #1\n");
return;
}
//CONS_Debug(DBG_NIGHTS, "Drawing a line from %d to ", axis->health);
transfer1line.v1->x = axis->x;
transfer1line.v1->y = axis->y;
transfer1line.v2->x = transfer1->x;
transfer1line.v2->y = transfer1->y;
if (cv_debug & DBG_NIGHTS)
P_ShootLine(axis, transfer1, player->mo->z);
//CONS_Debug(DBG_NIGHTS, "closest %d\n", transfer1->health);
transfer1line.dx = transfer1line.v2->x - transfer1line.v1->x;
transfer1line.dy = transfer1line.v2->y - transfer1line.v1->y;
if (P_PointOnLineSide(player->mo->x, player->mo->y, &transfer1line)
2014-08-03 20:49:33 -07:00
!= P_PointOnLineSide(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, &transfer1line)
&& truexspeed < 0)
2014-03-15 09:59:03 -07:00
{
if (cv_debug & DBG_NIGHTS)
{
HU_SetCEchoDuration(1);
HU_DoCEcho("transfer!");
HU_SetCEchoDuration(5);
S_StartSound(NULL, sfx_strpst);
}
if (player->pflags & PF_TRANSFERTOCLOSEST)
{
player->pflags &= ~PF_TRANSFERTOCLOSEST;
P_TransferToAxis(player, transfer1->health - 1);
}
else
{
player->pflags |= PF_TRANSFERTOCLOSEST;
P_SetTarget(&player->axis2, transfer1);
P_SetTarget(&player->axis1, P_FindAxisTransfer(transfer1->threshold, transfer1->health-1, MT_AXISTRANSFERLINE));//P_FindAxis(transfer1->threshold, axis->health-2);
}
}
}
else
{
// Transfer1
if (transfer1last)
axis = P_FindAxis(transfer1->threshold, 1);
else
axis = P_FindAxis(transfer1->threshold, transfer1->health);
if (!axis)
{
CONS_Debug(DBG_NIGHTS, "Unable to find an axis - error code #2\n");
return;
}
//CONS_Debug(DBG_NIGHTS, "Drawing a line from %d to ", axis->health);
transfer1line.v1->x = axis->x;
transfer1line.v1->y = axis->y;
if (cv_debug & DBG_NIGHTS)
P_ShootLine(transfer1, P_FindAxis(transfer1->threshold, transfer1->health-1), player->mo->z);
//axis = P_FindAxis(transfer1->threshold, transfer1->health-1);
//CONS_Debug(DBG_NIGHTS, "%d\n", axis->health);
transfer1line.v2->x = transfer1->x;
transfer1line.v2->y = transfer1->y;
transfer1line.dx = transfer1line.v2->x - transfer1line.v1->x;
transfer1line.dy = transfer1line.v2->y - transfer1line.v1->y;
if (P_PointOnLineSide(player->mo->x, player->mo->y, &transfer1line)
2014-08-03 20:49:33 -07:00
!= P_PointOnLineSide(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, &transfer1line)
&& truexspeed < 0)
2014-03-15 09:59:03 -07:00
{
if (cv_debug & DBG_NIGHTS)
{
HU_SetCEchoDuration(1);
HU_DoCEcho("transfer!");
HU_SetCEchoDuration(5);
S_StartSound(NULL, sfx_strpst);
}
if (player->mo->target->health < transfer1->health)
{
// Find the next axis with a ->health
// +1 from the current axis.
if (transfer1last)
P_TransferToAxis(player, transfer1->health - 1);
else
P_TransferToAxis(player, transfer1->health);
}
else if (player->mo->target->health >= transfer1->health)
{
// Find the next axis with a ->health
// -1 from the current axis.
P_TransferToAxis(player, transfer1->health - 1);
}
}
}
}
else
{
2014-08-03 20:49:33 -07:00
//CONS_Debug(DBG_NIGHTS, " must be > 0 to transfer\n");
2014-03-15 09:59:03 -07:00
if (transfer2->type == MT_AXISTRANSFERLINE)
{
if (transfer2last)
axis = P_FindAxis(transfer2->threshold, 1);
else if (player->pflags & PF_TRANSFERTOCLOSEST)
axis = P_FindAxis(transfer2->threshold, transfer2->health);
else
axis = P_FindAxis(transfer2->threshold, transfer2->health - 1);
if (!axis)
axis = P_FindAxis(transfer2->threshold, 1);
if (!axis)
{
CONS_Debug(DBG_NIGHTS, "Unable to find an axis - error code #3\n");
return;
}
//CONS_Debug(DBG_NIGHTS, "Drawing a line from %d to ", axis->health);
transfer2line.v1->x = axis->x;
transfer2line.v1->y = axis->y;
transfer2line.v2->x = transfer2->x;
transfer2line.v2->y = transfer2->y;
//CONS_Debug(DBG_NIGHTS, "closest %d\n", transfer2->health);
if (cv_debug & DBG_NIGHTS)
P_ShootLine(axis, transfer2, player->mo->z);
transfer2line.dx = transfer2line.v2->x - transfer2line.v1->x;
transfer2line.dy = transfer2line.v2->y - transfer2line.v1->y;
if (P_PointOnLineSide(player->mo->x, player->mo->y, &transfer2line)
2014-08-03 20:49:33 -07:00
!= P_PointOnLineSide(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, &transfer2line)
&& truexspeed > 0)
2014-03-15 09:59:03 -07:00
{
if (cv_debug & DBG_NIGHTS)
{
HU_SetCEchoDuration(1);
HU_DoCEcho("transfer!");
HU_SetCEchoDuration(5);
S_StartSound(NULL, sfx_strpst);
}
if (player->pflags & PF_TRANSFERTOCLOSEST)
{
player->pflags &= ~PF_TRANSFERTOCLOSEST;
if (!P_FindAxis(transfer2->threshold, transfer2->health))
transfer2last = true;
if (transfer2last)
P_TransferToAxis(player, 1);
else
P_TransferToAxis(player, transfer2->health);
}
else
{
player->pflags |= PF_TRANSFERTOCLOSEST;
P_SetTarget(&player->axis1, transfer2);
P_SetTarget(&player->axis2, P_FindAxisTransfer(transfer2->threshold, transfer2->health+1, MT_AXISTRANSFERLINE));//P_FindAxis(transfer2->threshold, axis->health + 2);
}
}
}
else
{
// Transfer2
if (transfer2last)
axis = P_FindAxis(transfer2->threshold, 1);
else
axis = P_FindAxis(transfer2->threshold, transfer2->health);
if (!axis)
axis = P_FindAxis(transfer2->threshold, 1);
if (!axis)
{
CONS_Debug(DBG_NIGHTS, "Unable to find an axis - error code #4\n");
return;
}
//CONS_Debug(DBG_NIGHTS, "Drawing a line from %d to ", axis->health);
transfer2line.v1->x = axis->x;
transfer2line.v1->y = axis->y;
if (cv_debug & DBG_NIGHTS)
P_ShootLine(transfer2, P_FindAxis(transfer2->threshold, transfer2->health-1), player->mo->z);
//axis = P_FindAxis(transfer2->threshold, transfer2->health-1);
//CONS_Debug(DBG_NIGHTS, "%d\n", axis->health);
transfer2line.v2->x = transfer2->x;
transfer2line.v2->y = transfer2->y;
transfer2line.dx = transfer2line.v2->x - transfer2line.v1->x;
transfer2line.dy = transfer2line.v2->y - transfer2line.v1->y;
if (P_PointOnLineSide(player->mo->x, player->mo->y, &transfer2line)
2014-08-03 20:49:33 -07:00
!= P_PointOnLineSide(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, &transfer2line)
&& truexspeed > 0)
2014-03-15 09:59:03 -07:00
{
if (cv_debug & DBG_NIGHTS)
{
HU_SetCEchoDuration(1);
HU_DoCEcho("transfer!");
HU_SetCEchoDuration(5);
S_StartSound(NULL, sfx_strpst);
}
if (player->mo->target->health < transfer2->health)
{
if (!P_FindAxis(transfer2->threshold, transfer2->health))
transfer2last = true;
if (transfer2last)
P_TransferToAxis(player, 1);
else
P_TransferToAxis(player, transfer2->health);
}
else if (player->mo->target->health >= transfer2->health)
P_TransferToAxis(player, transfer2->health - 1);
}
}
}
}
}
//
// P_DoNiGHTSCapsule
//
// Handles blowing up the Ideya (emerald) capsule for NiGHTS mode
//
static void P_DoNiGHTSCapsule(player_t *player)
{
INT32 i;
if ((player->pflags & PF_NIGHTSMODE) && (player->mo->tracer->state < &states[S_NIGHTSHURT1]
|| player->mo->tracer->state > &states[S_NIGHTSHURT32]))
P_SetMobjState(player->mo->tracer, S_NIGHTSHURT1);
if (abs(player->mo->x-player->capsule->x) <= 2*FRACUNIT)
{
P_UnsetThingPosition(player->mo);
player->mo->x = player->capsule->x;
P_SetThingPosition(player->mo);
player->mo->momx = 0;
}
if (abs(player->mo->y-player->capsule->y) <= 2*FRACUNIT)
{
P_UnsetThingPosition(player->mo);
player->mo->y = player->capsule->y;
P_SetThingPosition(player->mo);
player->mo->momy = 0;
}
if (abs(player->mo->z - (player->capsule->z+(player->capsule->height/3))) <= 2*FRACUNIT)
{
player->mo->z = player->capsule->z+(player->capsule->height/3);
player->mo->momz = 0;
}
if (player->mo->x > player->capsule->x)
player->mo->momx = -2*FRACUNIT;
else if (player->mo->x < player->capsule->x)
player->mo->momx = 2*FRACUNIT;
if (player->mo->y > player->capsule->y)
player->mo->momy = -2*FRACUNIT;
else if (player->mo->y < player->capsule->y)
player->mo->momy = 2*FRACUNIT;
if (player->mo->z > player->capsule->z+(player->capsule->height/3))
player->mo->momz = -2*FRACUNIT;
else if (player->mo->z < player->capsule->z+(player->capsule->height/3))
player->mo->momz = 2*FRACUNIT;
if (G_IsSpecialStage(gamemap))
{ // In special stages, share rings. Everyone gives up theirs to the capsule player always, because we can't have any individualism here!
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && (&players[i] != player) && players[i].mo->health > 1)
{
player->mo->health += players[i].mo->health-1;
player->health = player->mo->health;
players[i].mo->health = 1;
players[i].health = players[i].mo->health;
}
}
// Time to blow it up!
if (player->mo->x == player->capsule->x
&& player->mo->y == player->capsule->y
&& player->mo->z == player->capsule->z+(player->capsule->height/3))
{
if (player->mo->health > 1)
{
player->mo->health--;
player->health--;
player->capsule->health--;
player->capsule->extravalue1++;
// Spawn a 'pop' for every 5 rings you deposit
if (!(player->capsule->extravalue1 % 5))
S_StartSound(P_SpawnMobj(player->capsule->x + ((P_SignedRandom()/2)<<FRACBITS),
player->capsule->y + ((P_SignedRandom()/2)<<FRACBITS),
player->capsule->z + (player->capsule->height/2) + ((P_SignedRandom()/2)<<FRACBITS),
MT_EXPLODE),sfx_pop);
if (player->capsule->health <= 0)
{
player->capsule->flags &= ~MF_NOGRAVITY;
player->capsule->momz = 5*FRACUNIT;
player->capsule->reactiontime = 0;
player->capsule->extravalue1 = -1;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && !player->exiting && players[i].mare == player->mare)
{
players[i].bonustime = true;
players[i].texttimer = 4*TICRATE;
players[i].textvar = 1; // Time Bonus
players[i].finishedtime = players[i].nightstime;
if (!G_IsSpecialStage(gamemap))
P_AddPlayerScore(&players[i], (players[i].finishedtime/TICRATE) * 100);
P_FlashPal(&players[i], PAL_WHITE, 8);
}
if (G_IsSpecialStage(gamemap))
{
// The Chaos Emerald begins to orbit us!
mobj_t *emmo;
UINT8 em = P_GetNextEmerald();
tic_t lowest_time;
2014-08-03 20:49:33 -07:00
if (player->mo->tracer)
2014-03-15 09:59:03 -07:00
{
// Only give it to ONE person, and THAT player has to get to the goal!
emmo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->info->height, MT_GOTEMERALD);
P_SetTarget(&emmo->target, player->mo);
P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em);
P_SetTarget(&player->mo->tracer->target, emmo);
}
// Okay, we're doing this down here because we're handling time weirdly for co-op special stages
// and because P_AddPlayerScore gives score to everyone in co-op special stages.
// Find the player with the lowest time remaining and award points based on that time instead.
lowest_time = player->finishedtime;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].pflags & PF_NIGHTSMODE)
if (players[i].finishedtime < lowest_time)
lowest_time = players[i].finishedtime;
P_AddPlayerScore(player, (lowest_time/TICRATE) * 100);
}
else
{
fixed_t z;
z = player->capsule->z + player->capsule->height/2;
for (i = 0; i < 16; i++)
P_SpawnMobj(player->capsule->x, player->capsule->y, z, MT_BIRD);
}
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mare == player->mare)
P_SetTarget(&players[i].capsule, NULL); // Remove capsule from everyone now that it is dead!
S_StartScreamSound(player->mo, sfx_ngdone);
}
}
else
{
S_StartScreamSound(player->mo, sfx_lose);
player->texttimer = 4*TICRATE;
player->textvar = 3; // Get more rings!
player->capsule->reactiontime = 0;
player->capsule->extravalue1 = -1;
}
}
else
player->capsule->extravalue1 = -1;
}
//
// P_NiGHTSMovement
//
// Movement code for NiGHTS!
//
static void P_NiGHTSMovement(player_t *player)
{
fixed_t drillamt = 0;
boolean still = false, moved = false, backwardaxis = false, firstdrill;
INT16 newangle = 0;
fixed_t xspeed, yspeed;
thinker_t *th;
mobj_t *mo2;
mobj_t *closestaxis = NULL;
fixed_t newx, newy, radius;
angle_t movingangle;
ticcmd_t *cmd = &player->cmd;
INT32 thrustfactor;
INT32 i;
statenum_t flystate = S_NIGHTSFLY1A;
player->pflags &= ~PF_DRILLING;
firstdrill = false;
if (player->drillmeter > 96*20)
player->drillmeter = 96*20;
if (player->drilldelay)
player->drilldelay--;
if (!(cmd->buttons & BT_DRIFT))
2014-03-15 09:59:03 -07:00
{
// Always have just a TINY bit of drill power.
if (player->drillmeter <= 0)
player->drillmeter = TICRATE/10;
}
if (!player->mo->tracer)
{
P_DeNightserizePlayer(player);
return;
}
if (G_IsSpecialStage(gamemap))
{
boolean capsule = false;
// NiGHTS special stages have a pseudo-shared timer, so check if ANYONE is feeding the capsule.
for (i = 0; i < MAXPLAYERS; i++)
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
if (playeringame[i]
2014-03-15 09:59:03 -07:00
&& (players[i].capsule && players[i].capsule->reactiontime))
capsule = true;
if (!capsule
&& !(player->mo->tracer->state >= &states[S_SUPERTRANS1]
&& player->mo->tracer->state <= &states[S_SUPERTRANS9])
&& !player->exiting)
player->nightstime--;
}
else if (!G_RaceGametype()
2014-03-15 09:59:03 -07:00
&& !(player->mo->tracer->state >= &states[S_SUPERTRANS1] && player->mo->tracer->state <= &states[S_SUPERTRANS9])
&& !(player->capsule && player->capsule->reactiontime)
&& !player->exiting)
player->nightstime--;
if (!player->nightstime)
{
P_DeNightserizePlayer(player);
S_StartScreamSound(player->mo, sfx_s3k66);
// S_StopSoundByNum(sfx_timeup); // Kill the "out of time" music, if it's playing. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
P_RestoreMusic(player); // I have my doubts that this is the right place for this...
return;
}
else if (P_IsLocalPlayer(player) && player->nightstime == 10*TICRATE)
// S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
2016-07-05 21:09:17 -07:00
S_ChangeMusicInternal("drown",false);
2014-03-15 09:59:03 -07:00
if (player->mo->z < player->mo->floorz)
player->mo->z = player->mo->floorz;
if (player->mo->z+player->mo->height > player->mo->ceilingz)
player->mo->z = player->mo->ceilingz - player->mo->height;
newx = P_ReturnThrustX(player->mo, player->mo->angle, 3*FRACUNIT)+player->mo->x;
newy = P_ReturnThrustY(player->mo, player->mo->angle, 3*FRACUNIT)+player->mo->y;
if (!player->mo->target)
{
fixed_t dist1, dist2 = 0;
// scan the thinkers
// to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_AXIS)
{
if (mo2->threshold == player->mare)
{
if (closestaxis == NULL)
{
closestaxis = mo2;
dist2 = R_PointToDist2(newx, newy, mo2->x, mo2->y)-mo2->radius;
}
else
{
dist1 = R_PointToDist2(newx, newy, mo2->x, mo2->y)-mo2->radius;
if (dist1 < dist2)
{
closestaxis = mo2;
dist2 = dist1;
}
}
}
}
}
P_SetTarget(&player->mo->target, closestaxis);
}
if (!player->mo->target) // Uh-oh!
{
CONS_Debug(DBG_NIGHTS, "No axis points found!\n");
return;
}
// The 'ambush' flag says you should rotate
// the other way around the axis.
if (player->mo->target->flags2 & MF2_AMBUSH)
2014-03-15 09:59:03 -07:00
backwardaxis = true;
player->angle_pos = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
player->old_angle_pos = player->angle_pos;
radius = player->mo->target->radius;
player->mo->flags |= MF_NOGRAVITY;
player->mo->flags2 |= MF2_DONTDRAW;
P_SetScale(player->mo->tracer, player->mo->scale);
if (player->mo->eflags & MFE_VERTICALFLIP)
cmd->forwardmove = (SINT8)(-cmd->forwardmove);
2016-07-05 21:09:17 -07:00
if (!(player->pflags & PF_TRANSFERTOCLOSEST))
{
fixed_t realdist = R_PointToDist2(player->mo->x, player->mo->y, player->mo->target->x, player->mo->target->y);
// teleport player to correct radius if neccessary
if (realdist>>FRACBITS != radius>>FRACBITS)
{
CONS_Debug(DBG_NIGHTS, "Aligning player with axis\n");
P_UnsetThingPosition(player->mo);
if (realdist == 0) // other method won't work if we're exactly on the target lol
{
const angle_t fa = player->old_angle_pos>>ANGLETOFINESHIFT;
player->mo->x = player->mo->target->x + FixedMul(FINECOSINE(fa), radius);
player->mo->y = player->mo->target->y + FixedMul(FINESINE(fa), radius);
}
else
{
player->mo->x = player->mo->target->x + FixedMul(FixedDiv(player->mo->x - player->mo->target->x, realdist), radius);
player->mo->y = player->mo->target->y + FixedMul(FixedDiv(player->mo->y - player->mo->target->y, realdist), radius);
}
P_SetThingPosition(player->mo);
}
}
2014-03-15 09:59:03 -07:00
// Currently reeling from being hit.
if (player->powers[pw_flashing] > (2*K_GetKartFlashing(player))/3)
2014-03-15 09:59:03 -07:00
{
{
const angle_t fa = (FixedAngle(player->flyangle*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
const fixed_t speed = FixedDiv(player->speed*FRACUNIT,50*FRACUNIT);
xspeed = FixedMul(FINECOSINE(fa),speed);
yspeed = FixedMul(FINESINE(fa),speed);
}
if (!(player->pflags & PF_TRANSFERTOCLOSEST))
{
xspeed = FixedMul(xspeed, FixedDiv(1024*FRACUNIT, player->mo->target->radius));
if (backwardaxis)
xspeed *= -1;
player->angle_pos += FixedAngleC(FixedDiv(xspeed,5*FRACUNIT),40*FRACUNIT);
}
player->mo->momz = 0;
P_NightsTransferPoints(player, xspeed, radius);
return;
}
if (player->mo->tracer->state >= &states[S_SUPERTRANS1]
&& player->mo->tracer->state <= &states[S_SUPERTRANS9])
{
player->mo->momx = player->mo->momy = player->mo->momz = 0;
return;
}
if (player->exiting > 0 && player->exiting < 2*TICRATE)
{
player->mo->momx = player->mo->momy = 0;
if (!G_RaceGametype())
2014-03-15 09:59:03 -07:00
P_SetObjectMomZ(player->mo, 30*FRACUNIT, false);
player->mo->tracer->angle += ANGLE_11hh;
if (!(player->mo->tracer->state >= &states[S_NIGHTSDRONE1]
&& player->mo->tracer->state <= &states[S_NIGHTSDRONE2]))
P_SetMobjState(player->mo->tracer, S_NIGHTSDRONE1);
player->mo->tracer->flags |= MF_NOCLIPHEIGHT;
player->mo->flags |= MF_NOCLIPHEIGHT;
return;
}
// Spawn the little sparkles on each side of the player.
if (leveltime & 1)
{
mobj_t *firstmobj;
mobj_t *secondmobj;
fixed_t spawndist = FixedMul(16*FRACUNIT, player->mo->scale);
2014-08-03 20:49:33 -07:00
fixed_t z = player->mo->z + player->mo->height/2;
if (player->mo->eflags & MFE_VERTICALFLIP)
z -= FixedMul(mobjinfo[MT_NIGHTSPARKLE].height, player->mo->scale);
2014-03-15 09:59:03 -07:00
2014-08-03 20:49:33 -07:00
firstmobj = P_SpawnMobj(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle+ANGLE_90, spawndist), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle+ANGLE_90, spawndist), z, MT_NIGHTSPARKLE);
secondmobj = P_SpawnMobj(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle-ANGLE_90, spawndist), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle-ANGLE_90, spawndist), z, MT_NIGHTSPARKLE);
2014-03-15 09:59:03 -07:00
2014-08-03 20:49:33 -07:00
firstmobj->destscale = secondmobj->destscale = player->mo->scale;
2014-03-15 09:59:03 -07:00
P_SetTarget(&firstmobj->target, player->mo);
P_SetScale(firstmobj, player->mo->scale);
P_SetTarget(&secondmobj->target, player->mo);
P_SetScale(secondmobj, player->mo->scale);
// Superloop turns sparkles red
if (player->powers[pw_nights_superloop])
{
P_SetMobjState(firstmobj, mobjinfo[MT_NIGHTSPARKLE].seestate);
P_SetMobjState(secondmobj, mobjinfo[MT_NIGHTSPARKLE].seestate);
}
}
// Paraloop helper is now separate from sparkles
// It also spawns every tic to avoid failed paraloops
{
mobj_t *helpermobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_NIGHTSLOOPHELPER);
helpermobj->fuse = player->mo->fuse = leveltime;
P_SetTarget(&helpermobj->target, player->mo);
P_SetScale(helpermobj, player->mo->scale);
}
if (player->bumpertime)
{
player->jumping = 1;
player->pflags |= PF_DRILLING;
newangle = (INT16)player->flyangle;
}
else if (cmd->buttons & BT_DRIFT && player->drillmeter && player->drilldelay == 0)
2014-03-15 09:59:03 -07:00
{
if (!player->jumping)
firstdrill = true;
player->jumping = 1;
player->pflags |= PF_DRILLING;
}
else
{
player->jumping = 0;
if (cmd->sidemove != 0)
moved = true;
if (player->drillmeter & 1)
player->drillmeter++; // I'll be nice and give them one.
}
if (cmd->forwardmove != 0)
moved = true;
if (!player->bumpertime)
{
if (moved)
{
if (player->pflags & PF_DRILLING)
drillamt += 100*FRACUNIT;
else
{
const fixed_t fforward = abs(cmd->forwardmove)*FRACUNIT;
const fixed_t fside = abs(cmd->sidemove)*FRACUNIT;
const fixed_t dist = FixedHypot(fforward, fside);
drillamt += dist > 50*FRACUNIT ? 50*FRACUNIT : dist;
// Accel up from 50 * 2.5
drillamt = FixedMul(drillamt, 5*FRACUNIT/2);
}
if ((player->pflags & PF_DRILLING && player->speed < MAXDRILLSPEED) || player->speed < MAXNORMALSPEED)
player->speed += FixedInt(drillamt);
}
if (player->pflags & PF_DRILLING)
{
if (player->speed < MAXDRILLSPEED)
player->speed += 150;
if (--player->drillmeter == 0)
player->drilldelay = TICRATE*2;
}
if (player->speed < 0)
player->speed = 0;
if (!cmd->forwardmove)
{
if (cmd->sidemove > 0)
newangle = 0;
else if (cmd->sidemove < 0)
newangle = 180;
}
else if (!cmd->sidemove)
{
if (cmd->forwardmove > 0)
newangle = 90;
else if (cmd->forwardmove < 0)
newangle = 270;
}
else // AngleFixed(R_PointToAngle2()) results in slight inaccuracy! Don't use it unless movement is on both axises.
newangle = (INT16)FixedInt(AngleFixed(R_PointToAngle2(0,0, cmd->sidemove*FRACUNIT, cmd->forwardmove*FRACUNIT)));
if (newangle < 0 && moved)
newangle = (INT16)(360+newangle);
}
if (player->pflags & PF_DRILLING)
thrustfactor = 2;
else
{
thrustfactor = 8;
// Decelerate while turning normally.
if (moved && player->flyangle != newangle && player->speed > 12000)
player->speed -= 60;
}
for (i = 0; i < thrustfactor; i++)
{
if (moved && player->flyangle != newangle)
{
INT32 anglediff = (((newangle-player->flyangle)+360)%360);
INT32 angledif2 = (((player->flyangle-newangle)+360)%360);
// player->flyangle is the one to move
// newangle is the "move to"
if (anglediff == 0 && angledif2 == 0)
break;
if (anglediff>angledif2)
player->flyangle--;
else // if (anglediff<angledif2)
player->flyangle++;
}
// Buff out negatives, >360 angles...
player->flyangle = ((player->flyangle + 360) % 360);
}
if (!(player->speed)
&& cmd->forwardmove == 0)
still = true;
// No more bumper braking
if (!player->bumpertime
&& ((cmd->buttons & (BT_FORWARD|BT_BACKWARD)) == (BT_FORWARD|BT_BACKWARD)
|| (cmd->buttons & BT_BRAKE)))
2014-03-15 09:59:03 -07:00
{
if (!(player->pflags & PF_SKIDDOWN))
S_StartSound(player->mo, sfx_ngskid);
// You can tap the button to only slow down a bit,
// or hold it to slow to a crawl as before, your choice.
if (player->speed > 8000)
player->speed -= 2000;
else if (player->speed > 1000)
player->speed -= (player->speed/4);
else {
player->speed -= 60;
if (player->speed < 0)
{
player->speed = 0;
still = true;
}
}
player->pflags |= PF_SKIDDOWN;
}
else
player->pflags &= ~PF_SKIDDOWN;
{
const angle_t fa = (FixedAngle(player->flyangle*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
const fixed_t speed = FixedDiv(player->speed*FRACUNIT,50*FRACUNIT);
xspeed = FixedMul(FINECOSINE(fa),speed);
yspeed = FixedMul(FINESINE(fa),speed);
}
if (!(player->pflags & PF_TRANSFERTOCLOSEST))
{
xspeed = FixedMul(xspeed, FixedDiv(1024*FRACUNIT, player->mo->target->radius));
if (backwardaxis)
xspeed *= -1;
player->angle_pos += FixedAngleC(FixedDiv(xspeed,5*FRACUNIT),40*FRACUNIT);
}
P_NightsTransferPoints(player, xspeed, radius);
if (still)
player->mo->momz = -FRACUNIT;
else
player->mo->momz = yspeed/11;
if (player->mo->momz > 20*FRACUNIT)
player->mo->momz = 20*FRACUNIT;
else if (player->mo->momz < -20*FRACUNIT)
player->mo->momz = -20*FRACUNIT;
// You can create splashes as you fly across water.
2014-08-03 20:49:33 -07:00
if (((!(player->mo->eflags & MFE_VERTICALFLIP)
&& player->mo->z + P_GetPlayerHeight(player) >= player->mo->watertop && player->mo->z <= player->mo->watertop)
|| (player->mo->eflags & MFE_VERTICALFLIP
&& player->mo->z + player->mo->height - P_GetPlayerHeight(player) <= player->mo->waterbottom && player->mo->z + player->mo->height >= player->mo->waterbottom))
&& player->speed > 9000 && leveltime % (TICRATE/7) == 0 && !player->spectator)
2014-03-15 09:59:03 -07:00
{
2014-08-03 20:49:33 -07:00
mobj_t *water = P_SpawnMobj(player->mo->x, player->mo->y,
((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_SPLISH].height, player->mo->scale) : player->mo->watertop), MT_SPLISH);
2014-03-15 09:59:03 -07:00
if (player->mo->eflags & MFE_GOOWATER)
S_StartSound(water, sfx_ghit);
else
S_StartSound(water, sfx_wslap);
2014-08-03 20:49:33 -07:00
if (player->mo->eflags & MFE_VERTICALFLIP)
2014-03-15 09:59:03 -07:00
{
2014-08-03 20:49:33 -07:00
water->flags2 |= MF2_OBJECTFLIP;
water->eflags |= MFE_VERTICALFLIP;
2014-03-15 09:59:03 -07:00
}
2014-08-03 20:49:33 -07:00
water->destscale = player->mo->scale;
P_SetScale(water, player->mo->scale);
2014-03-15 09:59:03 -07:00
}
if (player->mo->momx || player->mo->momy)
player->mo->angle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
if (still)
{
player->anotherflyangle = 0;
movingangle = 0;
}
else
{
INT32 neg = 1;
// Special cases to prevent the angle from being
// calculated incorrectly when wrapped.
if (backwardaxis && (player->old_angle_pos > ANG350 && player->angle_pos < ANG10))
neg = -1;
else if (backwardaxis ^ (player->old_angle_pos < ANG10 && player->angle_pos > ANG350))
neg = -1;
else if (player->angle_pos > player->old_angle_pos)
neg = -1;
2014-08-03 20:49:33 -07:00
movingangle = R_PointToAngle2(0, 0, neg*R_PointToDist2(player->mo->momx, player->mo->momy, 0, 0), player->mo->momz);
2014-03-15 09:59:03 -07:00
player->anotherflyangle = (movingangle >> ANGLETOFINESHIFT) * 360/FINEANGLES;
}
// NiGHTS flying state
// Yep, I just ripped out almost 1000 lines of code.
if ((player->anotherflyangle >= 12 && player->anotherflyangle <= 33) // +x +y
|| (player->anotherflyangle >= 147 && player->anotherflyangle <= 168)) // -x +y
flystate = S_NIGHTSFLY2A;
else if ((player->anotherflyangle >= 34 && player->anotherflyangle <= 56) // +x +y
|| (player->anotherflyangle >= 124 && player->anotherflyangle <= 146)) // -x +y
flystate = S_NIGHTSFLY3A;
else if ((player->anotherflyangle >= 57 && player->anotherflyangle <= 79) // +x +y
|| (player->anotherflyangle >= 102 && player->anotherflyangle <= 123)) // -x +y
flystate = S_NIGHTSFLY4A;
else if (player->anotherflyangle >= 80 && player->anotherflyangle <= 101)
flystate = S_NIGHTSFLY5A;
else if ((player->anotherflyangle >= 192 && player->anotherflyangle <= 213) // -x -y
|| (player->anotherflyangle >= 327 && player->anotherflyangle <= 348)) // +x -y
flystate = S_NIGHTSFLY6A;
else if ((player->anotherflyangle >= 214 && player->anotherflyangle <= 236) // -x -y
|| (player->anotherflyangle >= 305 && player->anotherflyangle <= 326)) // +x -y
flystate = S_NIGHTSFLY7A;
else if ((player->anotherflyangle >= 237 && player->anotherflyangle <= 258) // -x -y
|| (player->anotherflyangle >= 282 && player->anotherflyangle <= 304)) // +x -y
flystate = S_NIGHTSFLY8A;
else if (player->anotherflyangle >= 259 && player->anotherflyangle <= 281)
flystate = S_NIGHTSFLY9A;
else
flystate = S_NIGHTSFLY1A;
if (player->mo->eflags & MFE_VERTICALFLIP)
{
if (flystate >= S_NIGHTSFLY2A && flystate <= S_NIGHTSFLY5A)
flystate += 24; // shift to S_NIGHTSFLY6A
else if (flystate >= S_NIGHTSFLY6A && flystate <= S_NIGHTSFLY9A)
flystate -= 24; // shift to S_NIGHTSFLY2A
}
if (player->pflags & PF_DRILLING)
{
const statenum_t drillstate = flystate + 2;
if (!(player->mo->tracer->state >= &states[drillstate]
&& player->mo->tracer->state <= &states[drillstate+4]))
{
if (!(player->mo->tracer->state >= &states[S_NIGHTSFLY1A]
&& player->mo->tracer->state <= &states[S_NIGHTSFLY9B]))
{
const INT32 framenum = player->mo->tracer->state->frame & 3;
if (framenum == 3) // Drilld special case
P_SetMobjStateNF(player->mo->tracer, drillstate);
else
P_SetMobjStateNF(player->mo->tracer, drillstate+framenum+1);
}
else
P_SetMobjStateNF(player->mo->tracer, drillstate);
}
}
else
P_SetMobjStateNF(player->mo->tracer, leveltime & 1 ? flystate : flystate+1);
if (player == &players[consoleplayer])
localangle[0] = player->mo->angle;
else if (player == &players[displayplayers[1]])
localangle[1] = player->mo->angle;
else if (player == &players[displayplayers[2]])
localangle[2] = player->mo->angle;
else if (player == &players[displayplayers[3]])
localangle[3] = player->mo->angle;
2014-03-15 09:59:03 -07:00
if (still)
{
P_SetMobjStateNF(player->mo->tracer, S_NIGHTSDRONE1);
player->mo->tracer->angle = player->mo->angle;
}
// Check for crushing in our new location
if ((player->mo->ceilingz - player->mo->floorz < player->mo->height)
&& !(player->mo->flags & MF_NOCLIP))
{
// Flashing won't run out until you STOP being crushed.
if (player->powers[pw_flashing] == 1)
player->powers[pw_flashing] = 3;
else
P_DamageMobj(player->mo, NULL, NULL, 1);
}
if (movingangle >= ANGLE_90 && movingangle <= ANGLE_180)
movingangle = movingangle - ANGLE_180;
else if (movingangle >= ANGLE_180 && movingangle <= ANGLE_270)
movingangle = movingangle - ANGLE_180;
else if (movingangle >= ANGLE_270)
movingangle = InvAngle(movingangle);
if (player == &players[consoleplayer])
localaiming[0] = movingangle;
else if (player == &players[displayplayers[1]])
localaiming[1] = movingangle;
else if (player == &players[displayplayers[2]])
localaiming[2] = movingangle;
else if (player == &players[displayplayers[3]])
localaiming[3] = movingangle;
2014-03-15 09:59:03 -07:00
player->mo->tracer->angle = player->mo->angle;
if ((player->pflags & PF_DRILLING) && !player->bumpertime)
{
if (firstdrill)
{
S_StartSound(player->mo, sfx_drill1);
player->drilltimer = 32;
}
else if (--player->drilltimer <= 0)
{
player->drilltimer = 10;
S_StartSound(player->mo, sfx_drill2);
}
}
if (objectplacing)
OP_NightsObjectplace(player);
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
}*/
2014-03-15 09:59:03 -07:00
// May be used in future for CTF
#if 0
static void P_PlayerDropWeapon(player_t *player)
{
mobj_t *mo = NULL;
if (player->powers[pw_homingring])
{
mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_HOMINGRING);
player->powers[pw_homingring] = 0;
}
else if (player->powers[pw_railring])
{
mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_RAILRING);
player->powers[pw_railring] = 0;
}
else if (player->powers[pw_automaticring])
{
mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_AUTOMATICRING);
player->powers[pw_automaticring] = 0;
}
else if (player->powers[pw_explosionring])
{
mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_EXPLOSIONRING);
player->powers[pw_explosionring] = 0;
}
else if (player->powers[pw_scatterring])
{
mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_SCATTERRING);
player->powers[pw_scatterring] = 0;
}
else if (player->powers[pw_grenadering])
{
mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+(60*FRACUNIT), MT_GRENADERING);
player->powers[pw_grenadering] = 0;
}
if (mo)
{
player->mo->health--;
P_InstaThrust(mo, player->mo->angle-ANGLE_180, 8*FRACUNIT);
P_SetObjectMomZ(mo, 4*FRACUNIT, false);
mo->flags2 |= MF2_DONTRESPAWN;
mo->flags &= ~MF_NOGRAVITY;
mo->flags &= ~MF_NOCLIPHEIGHT;
mo->fuse = 12*TICRATE;
}
}
#endif
2016-07-05 21:09:17 -07:00
void P_BlackOw(player_t *player)
{
INT32 i;
S_StartSound (player->mo, sfx_bkpoof); // Sound the BANG!
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && P_AproxDistance(player->mo->x - players[i].mo->x,
player->mo->y - players[i].mo->y) < 1536*FRACUNIT)
P_FlashPal(&players[i], PAL_NUKE, 10);
P_NukeEnemies(player->mo, player->mo, 1536*FRACUNIT); // Search for all nearby enemies and nuke their pants off!
player->powers[pw_shield] = player->powers[pw_shield] & SH_STACK;
}
void P_ElementalFireTrail(player_t *player)
{
fixed_t newx;
fixed_t newy;
2016-07-05 21:09:17 -07:00
fixed_t ground;
mobj_t *flame;
angle_t travelangle;
INT32 i;
2016-07-05 21:09:17 -07:00
I_Assert(player != NULL);
I_Assert(player->mo != NULL);
I_Assert(!P_MobjWasRemoved(player->mo));
2016-07-05 21:09:17 -07:00
if (player->mo->eflags & MFE_VERTICALFLIP)
ground = player->mo->ceilingz - FixedMul(mobjinfo[MT_SPINFIRE].height, player->mo->scale);
else
2016-07-05 21:09:17 -07:00
ground = player->mo->floorz;
travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
2016-07-05 21:09:17 -07:00
for (i = 0; i < 2; i++)
{
2016-07-05 21:09:17 -07:00
newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale));
newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale));
#ifdef ESLOPE
if (player->mo->standingslope)
{
2016-07-05 21:09:17 -07:00
ground = P_GetZAt(player->mo->standingslope, newx, newy);
if (player->mo->eflags & MFE_VERTICALFLIP)
ground -= FixedMul(mobjinfo[MT_SPINFIRE].height, player->mo->scale);
}
2016-07-05 21:09:17 -07:00
#endif
flame = P_SpawnMobj(newx, newy, ground, MT_SPINFIRE);
P_SetTarget(&flame->target, player->mo);
flame->angle = travelangle;
flame->fuse = TICRATE*6;
flame->destscale = player->mo->scale;
P_SetScale(flame, player->mo->scale);
flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
2016-07-05 21:09:17 -07:00
flame->momx = 8;
P_XYMovement(flame);
if (P_MobjWasRemoved(flame))
continue;
if (player->mo->eflags & MFE_VERTICALFLIP)
{
if (flame->z + flame->height < flame->ceilingz)
P_RemoveMobj(flame);
}
2016-07-05 21:09:17 -07:00
else if (flame->z > flame->floorz)
P_RemoveMobj(flame);
}
2016-07-05 21:09:17 -07:00
}
2014-03-15 09:59:03 -07:00
//
// P_MovePlayer
static void P_MovePlayer(player_t *player)
{
ticcmd_t *cmd;
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
//INT32 i;
2014-03-15 09:59:03 -07:00
fixed_t runspd;
if (countdowntimeup)
return;
2016-08-14 20:51:08 -07:00
/* // SRB2kart - junk
2014-03-15 09:59:03 -07:00
if (player->mo->state >= &states[S_PLAY_SUPERTRANS1] && player->mo->state <= &states[S_PLAY_SUPERTRANS9])
{
player->mo->momx = player->mo->momy = player->mo->momz = 0;
return;
}
2016-08-14 20:51:08 -07:00
*/
2014-03-15 09:59:03 -07:00
cmd = &player->cmd;
2017-11-07 15:35:05 -08:00
runspd = 14*player->mo->scale; //srb2kart
2014-03-15 09:59:03 -07:00
// Let's have some movement speed fun on low-friction surfaces, JUST for players...
// (high friction surfaces shouldn't have any adjustment, since the acceleration in
2016-08-23 18:03:58 -07:00
// this game is super high and that ends up cheesing high-friction surfaces.)
runspd = FixedMul(runspd, player->mo->movefactor);
2014-03-15 09:59:03 -07:00
// Control relinquishing stuff!
if (player->powers[pw_ingoop])
player->pflags |= PF_FULLSTASIS;
else if (player->pflags & PF_GLIDING && player->skidtime)
player->pflags |= PF_FULLSTASIS;
else if (player->powers[pw_nocontrol])
{
player->pflags |= PF_STASIS;
if (!(player->powers[pw_nocontrol] & (1<<15)))
player->pflags |= PF_JUMPSTASIS;
}
2016-07-05 21:09:17 -07:00
// note: don't unset stasis here
2014-03-15 09:59:03 -07:00
if (!player->spectator && G_TagGametype())
{
// If we have stasis already here, it's because it's forced on us
// by a linedef executor or what have you
boolean forcestasis = false;
//During hide time, taggers cannot move.
if (leveltime < hidetime * TICRATE)
{
if (player->pflags & PF_TAGIT)
forcestasis = true;
}
else if (gametype == GT_HIDEANDSEEK)
{
if (!(player->pflags & PF_TAGIT))
{
forcestasis = true;
if (player->pflags & PF_TAGGED) // Already hit.
player->powers[pw_flashing] = 5;
}
}
if (forcestasis)
{
player->pflags |= PF_FULLSTASIS;
// If you're in stasis in tag, you don't drown.
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
/*if (player->powers[pw_underwater] <= 12*TICRATE + 1)
P_RestoreMusic(player);*/
2014-03-15 09:59:03 -07:00
player->powers[pw_underwater] = player->powers[pw_spacetime] = 0;
}
}
2014-08-03 20:49:33 -07:00
if (player->spectator)
2014-03-15 09:59:03 -07:00
{
2014-08-03 20:49:33 -07:00
P_SpectatorMovement(player);
return;
2014-03-15 09:59:03 -07:00
}
// Locate the capsule for this mare.
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
/*else if (maptol & TOL_NIGHTS)
2014-03-15 09:59:03 -07:00
{
if (!player->capsule && !player->bonustime)
{
thinker_t *th;
mobj_t *mo2;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_EGGCAPSULE
&& mo2->threshold == player->mare)
P_SetTarget(&player->capsule, mo2);
}
}
else if (player->capsule && player->capsule->reactiontime > 0 && player == &players[player->capsule->reactiontime-1])
{
P_DoNiGHTSCapsule(player);
return;
}
// Test revamped NiGHTS movement.
if (player->pflags & PF_NIGHTSMODE)
{
P_NiGHTSMovement(player);
// No more goto blockchecking, let's just do all that here =D
if (CheckForBustableBlocks)
P_CheckBustableBlocks(player);
if (CheckForBouncySector)
P_CheckBouncySectors(player);
if (CheckForQuicksand)
P_CheckQuicksand(player);
return;
}
2014-08-03 20:49:33 -07:00
if (player->pflags & PF_NIGHTSFALL && P_IsObjectOnGround(player->mo))
2014-03-15 09:59:03 -07:00
{
if (G_IsSpecialStage(gamemap))
{
if (player == &players[displayplayers[0]]) // only play the sound for yourself landing
2014-03-15 09:59:03 -07:00
S_StartSound(NULL, sfx_s3k6a);
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
2018-09-22 15:59:26 -07:00
players[i].exiting = raceexittime+1;
2014-03-15 09:59:03 -07:00
}
else if (player->health > 1)
P_DamageMobj(player->mo, NULL, NULL, 1);
player->pflags &= ~PF_NIGHTSFALL;
}
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
}*/
2014-03-15 09:59:03 -07:00
//////////////////////
// MOVEMENT CODE //
//////////////////////
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
/*if (twodlevel || player->mo->flags2 & MF2_TWOD) // 2d-level, so special control applies.
2014-03-15 09:59:03 -07:00
P_2dMovement(player);
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
else*/
2014-03-15 09:59:03 -07:00
{
INT16 angle_diff, max_left_turn, max_right_turn;
boolean add_delta = true;
// Kart: store the current turn range for later use
if (((player->mo && player->speed > 0) // Moving
|| (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn
|| (player->kartstuff[k_respawn]) // Respawning
|| (player->spectator || objectplacing)) // Not a physical player
) // ~~Spinning and boosting cancels out turning~~ Not anymore given spinout is more slippery and more prone to get you killed because of boosters.
{
player->lturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, KART_FULLTURN)+1;
player->rturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, -KART_FULLTURN)-1;
} else {
player->lturn_max[leveltime%MAXPREDICTTICS] = player->rturn_max[leveltime%MAXPREDICTTICS] = 0;
}
if (leveltime >= starttime)
{
// KART: Don't directly apply angleturn! It may have been either A) forged by a malicious client, or B) not be a smooth turn due to a player dropping frames.
// Instead, turn the player only up to the amount they're supposed to turn accounting for latency. Allow exactly 1 extra turn unit to try to keep old replays synced.
angle_diff = cmd->angleturn - (player->mo->angle>>16);
max_left_turn = player->lturn_max[(leveltime + MAXPREDICTTICS - cmd->latency) % MAXPREDICTTICS];
max_right_turn = player->rturn_max[(leveltime + MAXPREDICTTICS - cmd->latency) % MAXPREDICTTICS];
//CONS_Printf("----------------\nangle diff: %d - turning options: %d to %d - ", angle_diff, max_left_turn, max_right_turn);
if (angle_diff > max_left_turn)
angle_diff = max_left_turn;
else if (angle_diff < max_right_turn)
angle_diff = max_right_turn;
else
{
// Try to keep normal turning as accurate to 1.0.1 as possible to reduce replay desyncs.
player->mo->angle = cmd->angleturn<<16;
add_delta = false;
}
//CONS_Printf("applied turn: %d\n", angle_diff);
if (add_delta) {
player->mo->angle += angle_diff<<16;
player->mo->angle &= ~0xFFFF; // Try to keep the turning somewhat similar to how it was before?
2019-01-08 18:36:06 -08:00
//CONS_Printf("leftover turn (%s): %5d or %4d%%\n",
// player_names[player-players],
// (INT16) (cmd->angleturn - (player->mo->angle>>16)),
2019-03-04 13:33:06 -08:00
// (INT16) (cmd->angleturn - (player->mo->angle>>16)) * 100 / (angle_diff ? angle_diff : 1));
}
}
2014-03-15 09:59:03 -07:00
ticruned++;
if ((cmd->angleturn & TICCMD_RECEIVED) == 0)
ticmiss++;
P_3dMovement(player);
}
if (maptol & TOL_2D)
runspd = FixedMul(runspd, 2*FRACUNIT/3);
//P_SkidStuff(player);
2014-03-15 09:59:03 -07:00
/////////////////////////
// MOVEMENT ANIMATIONS //
/////////////////////////
/*
2014-03-15 09:59:03 -07:00
if ((cmd->forwardmove != 0 || cmd->sidemove != 0) || (player->powers[pw_super] && player->mo->z > player->mo->floorz))
{
// If the player is moving fast enough,
// break into a run!
if (player->speed >= runspd && player->panim == PA_WALK && !player->skidtime && (onground || player->powers[pw_super]))
2016-08-14 20:51:08 -07:00
P_SetPlayerMobjState (player->mo, S_KART_RUN1); // SRB2kart - was S_PLAY_SPD1
2014-03-15 09:59:03 -07:00
// Otherwise, just walk.
else if ((player->rmomx || player->rmomy) && player->panim == PA_IDLE)
2016-08-14 20:51:08 -07:00
P_SetPlayerMobjState (player->mo, S_KART_WALK1); // SRB2kart - was S_PLAY_RUN1
2014-03-15 09:59:03 -07:00
}
*/
2016-08-14 20:51:08 -07:00
2014-03-15 09:59:03 -07:00
// If your running animation is playing, and you're
// going too slow, switch back to the walking frames.
//if (player->panim == PA_RUN && player->speed < runspd && player->kartstuff[k_spinouttimer] == 0)
//P_SetPlayerMobjState(player->mo, S_KART_WALK1); // SRB2kart - was S_PLAY_RUN1
2014-03-15 09:59:03 -07:00
// If Springing, but travelling DOWNWARD, change back!
2016-08-14 20:51:08 -07:00
//if (player->mo->state == &states[S_PLAY_SPRING] && P_MobjFlip(player->mo)*player->mo->momz < 0)
// P_SetPlayerMobjState(player->mo, S_PLAY_FALL1);
2014-03-15 09:59:03 -07:00
// If Springing but on the ground, change back!
2016-08-14 20:51:08 -07:00
//else if (onground && (player->mo->state == &states[S_PLAY_SPRING] || player->panim == PA_FALL || player->mo->state == &states[S_PLAY_CARRY]) && !player->mo->momz)
// P_SetPlayerMobjState(player->mo, S_PLAY_STND);
2014-03-15 09:59:03 -07:00
// Kart frames
if (player->kartstuff[k_squishedtimer] > 0)
{
if (player->mo->state != &states[S_KART_SQUISH])
P_SetPlayerMobjState(player->mo, S_KART_SQUISH);
}
else if (player->pflags & PF_SLIDING)
{
if (player->mo->state != &states[S_KART_SPIN])
P_SetPlayerMobjState(player->mo, S_KART_SPIN);
player->frameangle -= ANGLE_22h;
}
else if (player->kartstuff[k_spinouttimer] > 0)
{
INT32 speed = max(1, min(8, player->kartstuff[k_spinouttimer]/8));
if (player->mo->state != &states[S_KART_SPIN])
P_SetPlayerMobjState(player->mo, S_KART_SPIN);
2019-01-04 21:42:36 -08:00
if (speed == 1 && abs((signed)(player->mo->angle - player->frameangle)) < ANGLE_22h)
player->frameangle = player->mo->angle; // Face forward at the end of the animation
else
player->frameangle -= (ANGLE_11hh * speed);
}
else if (player->powers[pw_nocontrol] && player->pflags & PF_SKIDDOWN)
{
if (player->mo->state != &states[S_KART_SPIN])
P_SetPlayerMobjState(player->mo, S_KART_SPIN);
if (((player->powers[pw_nocontrol] + 5) % 20) < 10)
player->frameangle += ANGLE_11hh;
else
player->frameangle -= ANGLE_11hh;
}
else
{
K_KartMoveAnimation(player);
if (player->kartstuff[k_pogospring])
2017-11-06 21:57:42 -08:00
player->frameangle += ANGLE_22h;
else
player->frameangle = player->mo->angle;
}
2016-08-23 18:03:58 -07:00
player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame.
2014-03-15 09:59:03 -07:00
// If you are stopped and are still walking, stand still!
if (!player->mo->momx && !player->mo->momy && !player->mo->momz && player->panim == PA_WALK)
P_SetPlayerMobjState(player->mo, S_KART_STND1); // SRB2kart - was S_PLAY_STND
2016-08-14 20:51:08 -07:00
//{ SRB2kart
// Drifting sound
// Start looping the sound now.
if (leveltime % 50 == 0 && onground && player->kartstuff[k_drift] != 0)
S_StartSound(player->mo, sfx_drift);
// Leveltime being 50 might take a while at times. We'll start it up once, isntantly.
else if (!S_SoundPlaying(player->mo, sfx_drift) && onground && player->kartstuff[k_drift] != 0)
S_StartSound(player->mo, sfx_drift);
// Ok, we'll stop now.
else if (player->kartstuff[k_drift] == 0)
S_StopSoundByID(player->mo, sfx_drift);
K_MoveKartPlayer(player, onground);
2016-08-14 20:51:08 -07:00
//}
2014-03-15 09:59:03 -07:00
//////////////////
//GAMEPLAY STUFF//
//////////////////
// Make sure you're not "jumping" on the ground
if (onground && player->pflags & PF_JUMPED && !(player->pflags & PF_GLIDING)
&& P_MobjFlip(player->mo)*player->mo->momz < 0)
{
player->pflags &= ~PF_JUMPED;
player->jumping = 0;
player->secondjump = 0;
player->pflags &= ~PF_THOKKED;
P_SetPlayerMobjState(player->mo, S_KART_STND1); // SRB2kart - was S_PLAY_STND
2014-03-15 09:59:03 -07:00
}
2019-05-01 21:39:49 -07:00
if (/*!(player->charability == CA_GLIDEANDCLIMB) ||*/ player->gotflag) // If you can't glide, then why the heck would you be gliding?
2014-08-03 20:49:33 -07:00
{
2016-08-14 20:51:08 -07:00
/* // SRB2kart - ???
2014-08-03 20:49:33 -07:00
if (player->pflags & PF_GLIDING || player->climbing)
{
if (onground)
P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
else
{
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
}
}
2016-08-14 20:51:08 -07:00
*/
2014-08-03 20:49:33 -07:00
player->pflags &= ~PF_GLIDING;
player->glidetime = 0;
player->climbing = 0;
}
2014-03-15 09:59:03 -07:00
// Glide MOMZ
// AKA my own gravity. =)
2016-08-14 20:51:08 -07:00
/* // SRB2kart - gliding is illegal, go to jail
2014-03-15 09:59:03 -07:00
if (player->pflags & PF_GLIDING)
{
fixed_t leeway;
2014-04-13 22:14:58 -07:00
fixed_t glidespeed = player->actionspd;
if (player->powers[pw_super])
glidespeed *= 2;
2014-03-15 09:59:03 -07:00
if (player->mo->eflags & MFE_VERTICALFLIP)
{
if (player->mo->momz > FixedMul(2*FRACUNIT, player->mo->scale))
player->mo->momz -= FixedMul(3*(FRACUNIT/4), player->mo->scale);
}
else
{
if (player->mo->momz < FixedMul(-2*FRACUNIT, player->mo->scale))
player->mo->momz += FixedMul(3*(FRACUNIT/4), player->mo->scale);
}
// Strafing while gliding.
leeway = FixedAngle(cmd->sidemove*(FRACUNIT/2));
if (player->skidtime) // ground gliding
{
2014-04-13 22:14:58 -07:00
fixed_t speed = FixedMul(glidespeed, FRACUNIT - (FRACUNIT>>2));
2014-03-15 09:59:03 -07:00
if (player->mo->eflags & MFE_UNDERWATER)
speed >>= 1;
speed = FixedMul(speed - player->glidetime*FRACUNIT, player->mo->scale);
if (speed < 0)
speed = 0;
P_InstaThrust(player->mo, player->mo->angle-leeway, speed);
}
else if (player->mo->eflags & MFE_UNDERWATER)
2014-04-13 22:14:58 -07:00
P_InstaThrust(player->mo, player->mo->angle-leeway, FixedMul((glidespeed>>1) + player->glidetime*750, player->mo->scale));
2014-03-15 09:59:03 -07:00
else
2014-04-13 22:14:58 -07:00
P_InstaThrust(player->mo, player->mo->angle-leeway, FixedMul(glidespeed + player->glidetime*1500, player->mo->scale));
2014-03-15 09:59:03 -07:00
player->glidetime++;
if (!(player->pflags & PF_JUMPDOWN)) // If not holding the jump button
{
P_ResetPlayer(player); // down, stop gliding.
2014-08-03 20:49:33 -07:00
if (onground)
P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
else if ((player->charability2 == CA2_MULTIABILITY)
2014-03-15 09:59:03 -07:00
|| (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && player->charability == CA_GLIDEANDCLIMB))
{
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
}
else
{
2014-04-13 22:14:58 -07:00
player->pflags |= PF_THOKKED;
2014-03-15 09:59:03 -07:00
player->mo->momx >>= 1;
player->mo->momy >>= 1;
P_SetPlayerMobjState(player->mo, S_PLAY_FALL1);
}
}
}
else if (player->climbing) // 'Deceleration' for climbing on walls.
{
if (player->mo->momz > 0)
{
player->mo->momz -= FixedMul(FRACUNIT/2, player->mo->scale);
if (player->mo->momz < 0)
player->mo->momz = 0;
}
else if (player->mo->momz < 0)
{
player->mo->momz += FixedMul(FRACUNIT/2, player->mo->scale);
if (player->mo->momz > 0)
player->mo->momz = 0;
}
}
2016-08-14 20:51:08 -07:00
*/
2014-03-15 09:59:03 -07:00
// If you're running fast enough, you can create splashes as you run in shallow water.
if (!player->climbing
&& ((!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z + player->mo->height >= player->mo->watertop && player->mo->z <= player->mo->watertop)
|| (player->mo->eflags & MFE_VERTICALFLIP && player->mo->z + player->mo->height >= player->mo->waterbottom && player->mo->z <= player->mo->waterbottom))
&& (player->speed > runspd || (player->pflags & PF_STARTDASH))
&& leveltime % (TICRATE/7) == 0 && player->mo->momz == 0 && !(player->pflags & PF_SLIDING) && !player->spectator)
{
mobj_t *water = P_SpawnMobj(player->mo->x, player->mo->y,
((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_SPLISH].height, player->mo->scale) : player->mo->watertop), MT_SPLISH);
if (player->mo->eflags & MFE_GOOWATER)
S_StartSound(water, sfx_ghit);
else
S_StartSound(water, sfx_wslap);
if (player->mo->eflags & MFE_VERTICALFLIP)
{
water->flags2 |= MF2_OBJECTFLIP;
water->eflags |= MFE_VERTICALFLIP;
}
water->destscale = player->mo->scale;
P_SetScale(water, player->mo->scale);
}
// Little water sound while touching water - just a nicety.
if ((player->mo->eflags & MFE_TOUCHWATER) && !(player->mo->eflags & MFE_UNDERWATER) && !player->spectator)
{
2016-07-05 21:09:17 -07:00
if (P_RandomChance(FRACUNIT/2) && leveltime % TICRATE == 0)
2014-03-15 09:59:03 -07:00
S_StartSound(player->mo, sfx_floush);
}
2014-08-03 20:49:33 -07:00
////////////////
//TAILS FLYING//
////////////////
2016-08-14 20:51:08 -07:00
/* // SRB2kart - nah
2014-08-03 20:49:33 -07:00
if (!(player->charability == CA_FLY || player->charability == CA_SWIM)) // why are you flying when you cannot fly?!
2014-03-15 09:59:03 -07:00
{
2014-08-03 20:49:33 -07:00
if (player->powers[pw_tailsfly]
|| (player->mo->state >= &states[S_PLAY_SPC1] && player->mo->state <= &states[S_PLAY_SPC4]))
2014-03-15 09:59:03 -07:00
{
2014-08-03 20:49:33 -07:00
if (onground)
P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
else
{
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
}
2014-03-15 09:59:03 -07:00
}
2014-08-03 20:49:33 -07:00
player->powers[pw_tailsfly] = 0;
2014-03-15 09:59:03 -07:00
}
2014-08-03 20:49:33 -07:00
if (player->gotflag && player->powers[pw_tailsfly])
player->powers[pw_tailsfly] = 1;
2014-03-15 09:59:03 -07:00
// If not in a fly position, don't think you're flying!
if (player->panim != PA_ABILITY)
player->powers[pw_tailsfly] = 0;
2014-08-03 20:49:33 -07:00
if (player->charability == CA_FLY || (player->charability == CA_SWIM && player->mo->eflags & MFE_UNDERWATER))
2014-03-15 09:59:03 -07:00
{
// Fly counter for Tails.
if (player->powers[pw_tailsfly])
{
const fixed_t actionspd = player->actionspd/100;
if (player->charability2 == CA2_MULTIABILITY)
{
// Adventure-style flying by just holding the button down
if (cmd->buttons & BT_DRIFT && !(player->pflags & PF_STASIS) && !player->exiting)
2014-03-15 09:59:03 -07:00
P_SetObjectMomZ(player->mo, actionspd/4, true);
}
else
{
// Classic flying
if (player->fly1)
{
if (P_MobjFlip(player->mo)*player->mo->momz < FixedMul(5*actionspd, player->mo->scale))
P_SetObjectMomZ(player->mo, actionspd/2, true);
player->fly1--;
}
}
// Tails Put-Put noise
if (player->charability == CA_FLY && player->bot != 1 && leveltime % 10 == 0 && !player->spectator)
S_StartSound(player->mo, sfx_putput);
// Descend
if (cmd->buttons & BT_BRAKE && !(player->pflags & PF_STASIS) && !player->exiting)
2014-03-15 09:59:03 -07:00
if (P_MobjFlip(player->mo)*player->mo->momz > -FixedMul(5*actionspd, player->mo->scale))
P_SetObjectMomZ(player->mo, -actionspd/2, true);
}
else
{
// Tails-gets-tired Stuff
if (player->panim == PA_ABILITY)
P_SetPlayerMobjState(player->mo, S_PLAY_SPC4);
if (player->charability == CA_FLY && (leveltime % 10 == 0)
&& player->mo->state >= &states[S_PLAY_SPC1]
&& player->mo->state <= &states[S_PLAY_SPC4]
&& !player->spectator)
S_StartSound(player->mo, sfx_pudpud);
}
}
// End your chain if you're on the ground or climbing a wall.
// But not if invincible! Allow for some crazy long chains with it.
// Also keep in mind the PF_JUMPED check.
// If we lacked this, stepping up while jumping up would reset score.
// (for instance, when climbing up off a wall.)
if ((onground || player->climbing) && !(player->pflags & PF_JUMPED) && player->powers[pw_invulnerability] <= 1)
P_ResetScore(player);
// Show the "THOK!" graphic when spinning quickly across the ground. (even applies to non-spinners, in the case of zoom tubes)
if (player->pflags & PF_SPINNING && player->speed > FixedMul(15<<FRACBITS, player->mo->scale) && !(player->pflags & PF_JUMPED))
2014-04-13 22:14:58 -07:00
{
2014-03-15 09:59:03 -07:00
P_SpawnSpinMobj(player, player->spinitem);
if (demo.recording)
G_GhostAddSpin((INT32) (player - players));
2014-04-13 22:14:58 -07:00
}
2016-08-14 20:51:08 -07:00
*/
2014-03-15 09:59:03 -07:00
////////////////////////////
//SPINNING AND SPINDASHING//
////////////////////////////
// SRB2kart - Drifting smoke and fire
2018-06-09 19:50:21 -07:00
if (player->kartstuff[k_sneakertimer] > 0 && onground && (leveltime & 1))
K_SpawnBoostTrail(player);
if (player->kartstuff[k_invincibilitytimer] > 0)
K_SpawnSparkleTrail(player->mo);
if (player->kartstuff[k_wipeoutslow] > 1 && (leveltime & 1))
K_SpawnWipeoutTrail(player->mo, false);
K_DriftDustHandling(player->mo);
2016-08-14 20:51:08 -07:00
/* // SRB2kart - nadah
2014-03-15 09:59:03 -07:00
// If the player isn't on the ground, make sure they aren't in a "starting dash" position.
if (!onground)
{
player->pflags &= ~PF_STARTDASH;
player->dashspeed = 0;
}
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL
&& (player->pflags & PF_SPINNING) && player->speed > FixedMul(4<<FRACBITS, player->mo->scale) && onground && (leveltime & 1)
&& !(player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
P_ElementalFireTrail(player);
2016-07-05 21:09:17 -07:00
P_DoSpinDash(player, cmd);
2016-08-14 20:51:08 -07:00
*/
2014-03-15 09:59:03 -07:00
// jumping
//P_DoJumpStuff(player, cmd);
2014-03-15 09:59:03 -07:00
2016-08-14 20:51:08 -07:00
/*
2014-03-15 09:59:03 -07:00
// If you're not spinning, you'd better not be spindashing!
if (!(player->pflags & PF_SPINNING))
player->pflags &= ~PF_STARTDASH;
2016-08-14 20:51:08 -07:00
*/
2014-03-15 09:59:03 -07:00
//////////////////
//ANALOG CONTROL//
//////////////////
#if 0
2014-08-03 20:49:33 -07:00
// This really looks like it should be moved to P_3dMovement. -Red
2014-11-11 16:55:07 -08:00
if (P_AnalogMove(player)
2014-08-03 20:49:33 -07:00
&& (cmd->forwardmove != 0 || cmd->sidemove != 0) && !player->climbing && !twodlevel && !(player->mo->flags2 & MF2_TWOD))
2014-03-15 09:59:03 -07:00
{
// If travelling slow enough, face the way the controls
// point and not your direction of movement.
2014-08-03 20:49:33 -07:00
if (player->speed < FixedMul(5*FRACUNIT, player->mo->scale) || player->pflags & PF_GLIDING || !onground)
2014-03-15 09:59:03 -07:00
{
2014-08-03 20:49:33 -07:00
angle_t tempangle;
2014-03-15 09:59:03 -07:00
2014-11-11 16:55:07 -08:00
tempangle = (cmd->angleturn << 16);
#ifdef REDSANALOG // Ease to it. Chillax. ~Red
tempangle += R_PointToAngle2(0, 0, cmd->forwardmove*FRACUNIT, -cmd->sidemove*FRACUNIT);
{
fixed_t tweenvalue = max(abs(cmd->forwardmove), abs(cmd->sidemove));
if (tweenvalue < 10 && (cmd->buttons & (BT_FORWARD|BT_BACKWARD)) == (BT_FORWARD|BT_BACKWARD)) {
2014-11-11 16:55:07 -08:00
tempangle = (cmd->angleturn << 16);
tweenvalue = 16;
}
2014-03-15 09:59:03 -07:00
2014-11-11 16:55:07 -08:00
tweenvalue *= tweenvalue*tweenvalue*1536;
2014-03-15 09:59:03 -07:00
2014-11-11 16:55:07 -08:00
//if (player->pflags & PF_GLIDING)
//tweenvalue >>= 1;
tempangle -= player->mo->angle;
if (tempangle < ANGLE_180 && tempangle > tweenvalue)
player->mo->angle += tweenvalue;
else if (tempangle >= ANGLE_180 && InvAngle(tempangle) > tweenvalue)
player->mo->angle -= tweenvalue;
else
player->mo->angle += tempangle;
}
#else
// Less math this way ~Red
player->mo->angle = R_PointToAngle2(0, 0, cmd->forwardmove*FRACUNIT, -cmd->sidemove*FRACUNIT)+tempangle;
#endif
2014-03-15 09:59:03 -07:00
}
// Otherwise, face the direction you're travelling.
else if (player->panim == PA_WALK || player->panim == PA_RUN || player->panim == PA_ROLL
2019-05-01 21:39:49 -07:00
/*|| ((player->mo->state >= &states[S_PLAY_ABL1] && player->mo->state <= &states[S_PLAY_SPC4]) && player->charability == CA_FLY)*/) // SRB2kart - idk
2014-08-03 20:49:33 -07:00
player->mo->angle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
2014-03-15 09:59:03 -07:00
// Update the local angle control.
if (player == &players[consoleplayer])
localangle[0] = player->mo->angle;
else if (player == &players[displayplayers[1]])
localangle[1] = player->mo->angle;
else if (player == &players[displayplayers[2]])
localangle[2] = player->mo->angle;
else if (player == &players[displayplayers[3]])
localangle[3] = player->mo->angle;
2014-03-15 09:59:03 -07:00
}
#endif
2014-03-15 09:59:03 -07:00
///////////////////////////
//BOMB SHIELD ACTIVATION,//
//HOMING, AND OTHER COOL //
//STUFF! //
///////////////////////////
2016-08-14 20:51:08 -07:00
/* // SRB2kart - what's a shield? Never heard of it. Never happened. Nope.
if (cmd->buttons & BT_BRAKE) // Spin button effects
2014-03-15 09:59:03 -07:00
{
if (player->pflags & PF_JUMPED) // If the player is jumping
{
if (!(player->pflags & PF_USEDOWN)) // If the player is not holding down BT_BRAKE
2014-03-15 09:59:03 -07:00
{
// Jump shield activation
if (!P_PlayerInPain(player) // If the player is not in pain
&& !player->climbing // If the player is not climbing
&& !(player->pflags & (PF_GLIDING|PF_SLIDING|PF_THOKKED)) // If the player is not gliding or sliding and hasn't used their ability
&& !onground) // If the player isn't on the ground
{
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_JUMP && !player->powers[pw_super])
P_DoJumpShield(player);
else if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && player->charability == CA_FLY)
{
P_DoJumpShield(player);
player->mo->momz *= 2;
}
}
// Bomb shield activation
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB)
{
// Don't let Super Sonic or invincibility use it
if (!(player->powers[pw_super] || player->powers[pw_invulnerability]))
P_BlackOw(player);
}
}
// Super Sonic move
2016-07-05 21:09:17 -07:00
if (player->skin == 0 && player->powers[pw_super] && player->speed > FixedMul(5<<FRACBITS, player->mo->scale)
2014-03-15 09:59:03 -07:00
&& P_MobjFlip(player->mo)*player->mo->momz <= 0)
{
if (player->panim == PA_ROLL || player->mo->state == &states[S_PLAY_PAIN])
P_SetPlayerMobjState(player->mo, S_PLAY_SUPERWALK1);
player->mo->momz = 0;
player->pflags &= ~PF_SPINNING;
player->jumping = 0; // don't cut jump height after bouncing off something
}
}
}
// HOMING option.
if (player->charability == CA_HOMINGTHOK)
{
// If you've got a target, chase after it!
if (player->homing && player->mo->tracer)
{
P_SpawnThokMobj(player);
P_HomingAttack(player->mo, player->mo->tracer);
// But if you don't, then stop homing.
if (player->mo->tracer->health <= 0 || (player->mo->tracer->flags2 & MF2_FRET))
{
if (player->mo->eflags & MFE_UNDERWATER)
P_SetObjectMomZ(player->mo, FixedDiv(457*FRACUNIT,72*FRACUNIT), false);
else
P_SetObjectMomZ(player->mo, 10*FRACUNIT, false);
player->mo->momx = player->mo->momy = player->homing = 0;
if (player->mo->tracer->flags2 & MF2_FRET)
P_InstaThrust(player->mo, player->mo->angle, -(player->speed>>3));
if (!(player->mo->tracer->flags & MF_BOSS))
player->pflags &= ~PF_THOKKED;
}
}
// If you're not jumping, then you obviously wouldn't be homing.
if (!(player->pflags & PF_JUMPED))
player->homing = 0;
}
else
player->homing = 0;
if (player->climbing == 1)
P_DoClimbing(player);
if (player->climbing > 1)
{
P_InstaThrust(player->mo, player->mo->angle, FixedMul(4*FRACUNIT, player->mo->scale)); // Shove up against the wall
player->climbing--;
}
if (!player->climbing)
{
player->lastsidehit = -1;
player->lastlinehit = -1;
}
// Make sure you're not teetering when you shouldn't be.
if ((player->mo->state == &states[S_PLAY_TEETER1] || player->mo->state == &states[S_PLAY_TEETER2] || player->mo->state == &states[S_PLAY_SUPERTEETER])
&& (player->mo->momx || player->mo->momy || player->mo->momz))
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
// Check for teeter!
if (!(player->mo->momz || player->mo->momx || player->mo->momy) && !(player->mo->eflags & MFE_GOOWATER)
&& player->panim == PA_IDLE && !(player->pflags & (PF_CARRIED|PF_ITEMHANG|PF_ROPEHANG)))
P_DoTeeter(player);
// Toss a flag
if (G_GametypeHasTeams() && (cmd->buttons & BT_SPECTATE) && !(player->powers[pw_super]) && !(player->tossdelay))
2014-03-15 09:59:03 -07:00
{
if (!(player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
P_PlayerEmeraldBurst(player, true); // Toss emeralds
else
P_PlayerFlagBurst(player, true);
}
// check for fire
if (!player->exiting)
P_DoFiring(player, cmd);
{
fixed_t oldheight = player->mo->height;
// Less height while spinning. Good for spinning under things...?
if ((player->mo->state == &states[player->mo->info->painstate] || player->mo->state == &states[S_PLAY_SUPERHIT])
|| (player->charability2 == CA2_SPINDASH && (player->pflags & (PF_SPINNING|PF_JUMPED)))
|| player->powers[pw_tailsfly] || player->pflags & PF_GLIDING
|| (player->charability == CA_FLY && (player->mo->state >= &states[S_PLAY_SPC1] && player->mo->state <= &states[S_PLAY_SPC4])))
player->mo->height = P_GetPlayerSpinHeight(player);
else
player->mo->height = P_GetPlayerHeight(player);
if (player->mo->eflags & MFE_VERTICALFLIP && player->mo->height != oldheight) // adjust z height for reverse gravity, similar to how it's done for scaling
player->mo->z -= player->mo->height - oldheight;
}
2016-08-14 20:51:08 -07:00
*/
2014-03-15 09:59:03 -07:00
// Crush test...
if ((player->mo->ceilingz - player->mo->floorz < player->mo->height)
&& !(player->mo->flags & MF_NOCLIP))
{
2016-08-14 20:51:08 -07:00
/* // SRB2kart - no, we're not making the playerspin
2014-03-15 09:59:03 -07:00
if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_SPINNING))
{
player->pflags |= PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
}
2016-08-14 20:51:08 -07:00
else */if (player->mo->ceilingz - player->mo->floorz < player->mo->height)
2014-03-15 09:59:03 -07:00
{
if ((netgame || multiplayer) && player->spectator)
P_DamageMobj(player->mo, NULL, NULL, 42000); // Respawn crushed spectators
else
{
K_SquishPlayer(player, NULL, NULL); // SRB2kart - we don't kill when squished, we squish when squished.
2016-08-14 20:51:08 -07:00
/*
2014-03-15 09:59:03 -07:00
mobj_t *killer = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_NULL);
killer->threshold = 44; // Special flag that it was crushing which killed you.
P_DamageMobj(player->mo, killer, killer, 10000);
2016-08-14 20:51:08 -07:00
*/
2014-03-15 09:59:03 -07:00
}
if (player->playerstate == PST_DEAD)
return;
}
}
#ifdef HWRENDER
if (rendermode != render_soft && rendermode != render_none && cv_grfovchange.value)
{
fixed_t speed;
const fixed_t runnyspeed = 20*FRACUNIT;
2014-08-03 20:49:33 -07:00
speed = R_PointToDist2(player->rmomx, player->rmomy, 0, 0);
2014-03-15 09:59:03 -07:00
if (speed > K_GetKartSpeed(player, false)-(5<<FRACBITS))
speed = K_GetKartSpeed(player, false)-(5<<FRACBITS);
2014-03-15 09:59:03 -07:00
if (speed >= runnyspeed)
player->fovadd = speed-runnyspeed;
else
2014-08-03 20:49:33 -07:00
player->fovadd = 0;
2014-03-15 09:59:03 -07:00
2014-08-03 20:49:33 -07:00
if (player->fovadd < 0)
player->fovadd = 0;
2014-03-15 09:59:03 -07:00
}
else
2014-08-03 20:49:33 -07:00
player->fovadd = 0;
2014-03-15 09:59:03 -07:00
#endif
#ifdef FLOORSPLATS
if (cv_shadow.value && rendermode == render_soft)
R_AddFloorSplat(player->mo->subsector, player->mo, "SHADOW", player->mo->x,
player->mo->y, player->mo->floorz, SPLATDRAWMODE_OPAQUE);
#endif
// Look for blocks to bust up
// Because of FF_SHATTER, we should look for blocks constantly,
// not just when spinning or playing as Knuckles
if (CheckForBustableBlocks)
P_CheckBustableBlocks(player);
// Check for a BOUNCY sector!
if (CheckForBouncySector)
P_CheckBouncySectors(player);
// Look for Quicksand!
if (CheckForQuicksand)
P_CheckQuicksand(player);
}
static void P_DoZoomTube(player_t *player)
{
INT32 sequence;
fixed_t speed;
thinker_t *th;
mobj_t *mo2;
mobj_t *waypoint = NULL;
fixed_t dist;
boolean reverse;
//player->mo->height = P_GetPlayerSpinHeight(player);
2014-03-15 09:59:03 -07:00
if (player->speed > 0)
reverse = false;
else
reverse = true;
player->powers[pw_flashing] = 1;
speed = abs(player->speed);
sequence = player->mo->tracer->threshold;
// change slope
dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - player->mo->z);
if (dist < 1)
dist = 1;
player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - player->mo->z, dist), (speed));
2014-03-15 09:59:03 -07:00
// Calculate the distance between the player and the waypoint
// 'dist' already equals this.
// Will the player go past the waypoint?
if (speed > dist)
2014-03-15 09:59:03 -07:00
{
speed -= dist;
2014-03-15 09:59:03 -07:00
// If further away, set XYZ of player to waypoint location
P_UnsetThingPosition(player->mo);
player->mo->x = player->mo->tracer->x;
player->mo->y = player->mo->tracer->y;
player->mo->z = player->mo->tracer->z;
P_SetThingPosition(player->mo);
// ugh, duh!!
player->mo->floorz = player->mo->subsector->sector->floorheight;
player->mo->ceilingz = player->mo->subsector->sector->ceilingheight;
CONS_Debug(DBG_GAMELOGIC, "Looking for next waypoint...\n");
// Find next waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
continue;
mo2 = (mobj_t *)th;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == sequence)
{
if ((reverse && mo2->health == player->mo->tracer->health - 1)
|| (!reverse && mo2->health == player->mo->tracer->health + 1))
{
waypoint = mo2;
break;
}
}
}
if (waypoint)
{
CONS_Debug(DBG_GAMELOGIC, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health);
P_SetTarget(&player->mo->tracer, waypoint);
2014-03-15 09:59:03 -07:00
// calculate MOMX/MOMY/MOMZ for next waypoint
2014-03-15 09:59:03 -07:00
// change slope
dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - player->mo->z);
if (dist < 1)
dist = 1;
player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - player->mo->z, dist), (speed));
}
else
{
P_SetTarget(&player->mo->tracer, NULL); // Else, we just let them fly.
2014-03-15 09:59:03 -07:00
CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found, releasing from track...\n");
}
}
// change angle
if (player->mo->tracer)
{
player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->tracer->x, player->mo->tracer->y);
if (player == &players[consoleplayer])
localangle[0] = player->mo->angle;
else if (player == &players[displayplayers[1]])
localangle[1] = player->mo->angle;
else if (player == &players[displayplayers[2]])
localangle[2] = player->mo->angle;
else if (player == &players[displayplayers[3]])
localangle[3] = player->mo->angle;
2014-03-15 09:59:03 -07:00
}
#if 0
if (player->mo->state != &states[S_KART_SPIN])
P_SetPlayerMobjState(player->mo, S_KART_SPIN);
player->frameangle -= ANGLE_22h;
#endif
2014-03-15 09:59:03 -07:00
}
//
// P_DoRopeHang
//
// Kinda like P_DoZoomTube
// but a little different.
//
/*
static void P_DoRopeHang(player_t *player) // SRB2kart - unused.
2014-03-15 09:59:03 -07:00
{
INT32 sequence;
fixed_t speed;
thinker_t *th;
mobj_t *mo2;
mobj_t *waypoint = NULL;
fixed_t dist;
fixed_t playerz;
player->mo->height = P_GetPlayerHeight(player);
// Play the 'clink' sound only if the player is moving.
if (!(leveltime & 7) && player->speed)
S_StartSound(player->mo, sfx_s3k55);
playerz = player->mo->z + player->mo->height;
speed = abs(player->speed);
sequence = player->mo->tracer->threshold;
// change slope
dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - playerz);
if (dist < 1)
dist = 1;
player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - playerz, dist), (speed));
if (player->cmd.buttons & BT_USE && !(player->pflags & PF_STASIS)) // Drop off of the rope
{
P_SetTarget(&player->mo->tracer, NULL);
player->pflags |= PF_JUMPED;
player->pflags &= ~PF_ROPEHANG;
if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
&& !(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH)
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
return;
}
2014-03-15 09:59:03 -07:00
// If not allowed to move, we're done here.
if (!speed)
return;
// Calculate the distance between the player and the waypoint
// 'dist' already equals this.
// Will the player go past the waypoint?
if (speed > dist)
2014-03-15 09:59:03 -07:00
{
speed -= dist;
2014-03-15 09:59:03 -07:00
// If further away, set XYZ of player to waypoint location
P_UnsetThingPosition(player->mo);
player->mo->x = player->mo->tracer->x;
player->mo->y = player->mo->tracer->y;
player->mo->z = player->mo->tracer->z - player->mo->height;
playerz = player->mo->tracer->z;
2014-03-15 09:59:03 -07:00
P_SetThingPosition(player->mo);
CONS_Debug(DBG_GAMELOGIC, "Looking for next waypoint...\n");
// Find next waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
continue;
mo2 = (mobj_t *)th;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == sequence)
{
if (mo2->health == player->mo->tracer->health + 1)
{
waypoint = mo2;
break;
}
}
}
if (!(player->mo->tracer->flags & MF_SLIDEME) && !waypoint)
{
CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found, wrapping to start...\n");
// Wrap around back to first waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
continue;
mo2 = (mobj_t *)th;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == sequence)
{
if (mo2->health == 0)
{
waypoint = mo2;
break;
}
}
}
}
if (waypoint)
{
CONS_Debug(DBG_GAMELOGIC, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health);
P_SetTarget(&player->mo->tracer, waypoint);
2014-03-15 09:59:03 -07:00
// calculate MOMX/MOMY/MOMZ for next waypoint
// change slope
dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - playerz);
if (dist < 1)
dist = 1;
player->mo->momx = FixedMul(FixedDiv(player->mo->tracer->x - player->mo->x, dist), (speed));
player->mo->momy = FixedMul(FixedDiv(player->mo->tracer->y - player->mo->y, dist), (speed));
player->mo->momz = FixedMul(FixedDiv(player->mo->tracer->z - playerz, dist), (speed));
}
else
{
if (player->mo->tracer->flags & MF_SLIDEME)
{
player->pflags |= PF_JUMPED;
player->pflags &= ~PF_ROPEHANG;
if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
&& !(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH)
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
}
P_SetTarget(&player->mo->tracer, NULL);
CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found!\n");
}
}
}
*/
2014-03-15 09:59:03 -07:00
#if 0
//
// P_NukeAllPlayers
//
// Hurts all players
// source = guy who gets the credit
//
static void P_NukeAllPlayers(player_t *player)
{
mobj_t *mo;
thinker_t *think;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
mo = (mobj_t *)think;
if (!mo->player)
continue;
if (mo->health <= 0) // dead
continue;
if (mo == player->mo)
continue;
P_DamageMobj(mo, player->mo, player->mo, 1);
}
CONS_Printf(M_GetText("%s caused a world of pain.\n"), player_names[player-players]);
return;
}
#endif
//
// P_NukeEnemies
// Looks for something you can hit - Used for bomb shield
//
void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
{
const fixed_t ns = 60 * mapobjectscale;
2014-03-15 09:59:03 -07:00
mobj_t *mo;
angle_t fa;
thinker_t *think;
INT32 i;
radius = FixedMul(radius, mapobjectscale);
2014-03-15 09:59:03 -07:00
for (i = 0; i < 16; i++)
{
fa = (i*(FINEANGLES/16));
mo = P_SpawnMobj(inflictor->x, inflictor->y, inflictor->z, MT_SUPERSPARK);
2014-08-05 16:59:40 -07:00
if (!P_MobjWasRemoved(mo))
{
mo->momx = FixedMul(FINESINE(fa),ns);
mo->momy = FixedMul(FINECOSINE(fa),ns);
}
2014-03-15 09:59:03 -07:00
}
for (think = thinkercap.next; think != &thinkercap; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
mo = (mobj_t *)think;
2018-10-22 14:08:34 -07:00
if (!(mo->flags & MF_SHOOTABLE) && !(mo->type == MT_EGGGUARD || mo->type == MT_MINUS
|| mo->type == MT_SPB)) // Don't want to give SPB MF_SHOOTABLE, to ensure it's undamagable through other means
2014-03-15 09:59:03 -07:00
continue;
if (mo->flags & MF_MONITOR)
continue; // Monitors cannot be 'nuked'.
//if (!G_BattleGametype() && mo->type == MT_PLAYER)
// continue; // Don't hurt players in Co-Op!
2014-03-15 09:59:03 -07:00
2014-08-03 20:49:33 -07:00
if (abs(inflictor->x - mo->x) > radius || abs(inflictor->y - mo->y) > radius || abs(inflictor->z - mo->z) > radius)
continue; // Workaround for possible integer overflow in the below -Red
2014-03-15 09:59:03 -07:00
if (P_AproxDistance(P_AproxDistance(inflictor->x - mo->x, inflictor->y - mo->y), inflictor->z - mo->z) > radius)
continue;
if (mo->type == MT_MINUS && !(mo->flags & (MF_SPECIAL|MF_SHOOTABLE)))
mo->flags |= MF_SPECIAL|MF_SHOOTABLE;
if (mo->type == MT_EGGGUARD && mo->tracer) //nuke Egg Guard's shield!
P_KillMobj(mo->tracer, inflictor, source);
if (mo->flags & MF_BOSS) //don't OHKO bosses!
P_DamageMobj(mo, inflictor, source, 1);
//{ SRB2kart
2018-07-22 17:55:18 -07:00
if (mo->type == MT_ORBINAUT || mo->type == MT_JAWZ || mo->type == MT_JAWZ_DUD
|| mo->type == MT_ORBINAUT_SHIELD || mo->type == MT_JAWZ_SHIELD
2018-05-31 17:51:05 -07:00
|| mo->type == MT_BANANA || mo->type == MT_BANANA_SHIELD
|| mo->type == MT_EGGMANITEM || mo->type == MT_EGGMANITEM_SHIELD
|| mo->type == MT_BALLHOG || mo->type == MT_SPB)
{
if (mo->eflags & MFE_VERTICALFLIP)
mo->z -= mo->height;
else
mo->z += mo->height;
S_StartSound(mo, mo->info->deathsound);
P_KillMobj(mo, inflictor, source);
P_SetObjectMomZ(mo, 8*FRACUNIT, false);
P_InstaThrust(mo, R_PointToAngle2(inflictor->x, inflictor->y, mo->x, mo->y)+ANGLE_90, 16*FRACUNIT);
}
if (mo->type == MT_SPB) // If you destroy a SPB, you don't get the luxury of a cooldown.
{
spbplace = -1;
indirectitemcooldown = 0;
}
if (mo == inflictor) // Don't nuke yourself, dummy!
continue;
if (mo->type == MT_PLAYER) // Players wipe out in Kart
K_SpinPlayer(mo->player, source, 0, inflictor, false);
//}
2014-03-15 09:59:03 -07:00
else
P_DamageMobj(mo, inflictor, source, 1000);
}
}
//
// P_LookForEnemies
// Looks for something you can hit - Used for homing attack
// Includes monitors and springs!
//
boolean P_LookForEnemies(player_t *player)
{
mobj_t *mo;
thinker_t *think;
mobj_t *closestmo = NULL;
angle_t an;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
mo = (mobj_t *)think;
2014-04-13 22:14:58 -07:00
if (!(mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR|MF_SPRING)))
2014-03-15 09:59:03 -07:00
continue; // not a valid enemy
if (mo->health <= 0) // dead
continue;
if (mo == player->mo)
continue;
if (mo->flags2 & MF2_FRET)
continue;
2014-04-13 22:14:58 -07:00
if ((mo->flags & (MF_ENEMY|MF_BOSS)) && !(mo->flags & MF_SHOOTABLE)) // don't aim at something you can't shoot at anyway (see Egg Guard or Minus)
2014-03-15 09:59:03 -07:00
continue;
if (mo->type == MT_DETON) // Don't be STUPID, Sonic!
continue;
if (((mo->z > player->mo->z+FixedMul(MAXSTEPMOVE, mapobjectscale)) && !(player->mo->eflags & MFE_VERTICALFLIP))
|| ((mo->z+mo->height < player->mo->z+player->mo->height-FixedMul(MAXSTEPMOVE, mapobjectscale)) && (player->mo->eflags & MFE_VERTICALFLIP))) // Reverse gravity check - Flame.
2014-03-15 09:59:03 -07:00
continue; // Don't home upwards!
if (P_AproxDistance(P_AproxDistance(player->mo->x-mo->x, player->mo->y-mo->y),
player->mo->z-mo->z) > FixedMul(RING_DIST, player->mo->scale))
continue; // out of range
if ((twodlevel || player->mo->flags2 & MF2_TWOD)
&& abs(player->mo->y-mo->y) > player->mo->radius)
continue; // not in your 2d plane
if (mo->type == MT_PLAYER) // Don't chase after other players!
continue;
if (closestmo && P_AproxDistance(P_AproxDistance(player->mo->x-mo->x, player->mo->y-mo->y),
player->mo->z-mo->z) > P_AproxDistance(P_AproxDistance(player->mo->x-closestmo->x,
player->mo->y-closestmo->y), player->mo->z-closestmo->z))
continue;
an = R_PointToAngle2(player->mo->x, player->mo->y, mo->x, mo->y) - player->mo->angle;
if (an > ANGLE_90 && an < ANGLE_270)
continue; // behind back
if (!P_CheckSight(player->mo, mo))
continue; // out of sight
closestmo = mo;
}
if (closestmo)
{
// Found a target monster
P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, closestmo));
2016-07-05 21:09:17 -07:00
player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, closestmo->x, closestmo->y);
2014-03-15 09:59:03 -07:00
return true;
}
return false;
}
void P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target
{
fixed_t dist;
2014-08-03 20:49:33 -07:00
fixed_t ns = 0;
2014-03-15 09:59:03 -07:00
if (!enemy)
return;
if (!(enemy->health))
return;
// change angle
source->angle = R_PointToAngle2(source->x, source->y, enemy->x, enemy->y);
if (source->player)
2014-03-15 09:59:03 -07:00
{
if (source->player == &players[consoleplayer])
localangle[0] = source->angle;
else if (source->player == &players[displayplayers[1]])
localangle[1] = source->angle;
else if (source->player == &players[displayplayers[2]])
localangle[2] = source->angle;
else if (source->player == &players[displayplayers[3]])
localangle[3] = source->angle;
2014-03-15 09:59:03 -07:00
}
// change slope
dist = P_AproxDistance(P_AproxDistance(enemy->x - source->x, enemy->y - source->y),
enemy->z - source->z);
if (dist < 1)
dist = 1;
2014-04-13 22:14:58 -07:00
if (source->type == MT_DETON && enemy->player) // For Deton Chase (Unused)
ns = FixedDiv(FixedMul(K_GetKartSpeed(enemy->player, false), enemy->scale), FixedDiv(20*FRACUNIT,17*FRACUNIT));
2014-03-15 09:59:03 -07:00
else if (source->type != MT_PLAYER)
{
if (source->threshold == 32000)
2014-08-03 20:49:33 -07:00
ns = FixedMul(source->info->speed/2, source->scale);
2014-03-15 09:59:03 -07:00
else
2014-08-03 20:49:33 -07:00
ns = FixedMul(source->info->speed, source->scale);
2014-03-15 09:59:03 -07:00
}
else if (source->player)
ns = FixedDiv(FixedMul(K_GetKartSpeed(source->player, false), source->scale), 3*FRACUNIT/2);
2014-08-03 20:49:33 -07:00
source->momx = FixedMul(FixedDiv(enemy->x - source->x, dist), ns);
source->momy = FixedMul(FixedDiv(enemy->y - source->y, dist), ns);
source->momz = FixedMul(FixedDiv(enemy->z - source->z, dist), ns);
2014-03-15 09:59:03 -07:00
}
// Search for emeralds
void P_FindEmerald(void)
{
thinker_t *th;
mobj_t *mo2;
hunt1 = hunt2 = hunt3 = NULL;
// scan the remaining thinkers
// to find all emeralds
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_EMERHUNT)
{
if (!hunt1)
hunt1 = mo2;
else if (!hunt2)
hunt2 = mo2;
else if (!hunt3)
hunt3 = mo2;
}
}
return;
}
//
// P_DeathThink
// Fall on your face when dying.
// Decrease POV height to floor height.
//
static void P_DeathThink(player_t *player)
{
2019-01-05 22:00:30 -08:00
//ticcmd_t *cmd = &player->cmd;
//player->deltaviewheight = 0;
2014-03-15 09:59:03 -07:00
if (player->deadtimer < INT32_MAX)
player->deadtimer++;
if (player->bot) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already
goto notrealplayer;
2018-10-23 14:48:09 -07:00
if (player->pflags & PF_TIMEOVER)
{
player->kartstuff[k_timeovercam]++;
if (player->mo)
{
player->mo->flags |= (MF_NOGRAVITY|MF_NOCLIP);
player->mo->flags2 |= MF2_DONTDRAW;
}
}
else
player->kartstuff[k_timeovercam] = 0;
2014-03-15 09:59:03 -07:00
K_KartPlayerHUDUpdate(player);
2014-03-15 09:59:03 -07:00
// Force respawn if idle for more than 30 seconds in shooter modes.
if (player->lives > 0 /*&& leveltime >= starttime*/) // *could* you respawn?
2014-03-15 09:59:03 -07:00
{
// SRB2kart - spawn automatically after 1 second
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
if (player->deadtimer > ((netgame || multiplayer)
? cv_respawntime.value*TICRATE
: TICRATE)) // don't let them change it in record attack
player->playerstate = PST_REBORN;
2014-03-15 09:59:03 -07:00
}
2017-10-22 00:06:35 -07:00
// Keep time rolling
if (!(exitcountdown && !racecountdown) && !(player->exiting || mapreset) && !(player->pflags & PF_TIMEOVER))
2014-03-15 09:59:03 -07:00
{
if (leveltime >= starttime)
{
player->realtime = leveltime - starttime;
if (player == &players[consoleplayer])
{
if (player->spectator || !circuitmap)
curlap = 0;
else
curlap++; // This is too complicated to sync to realtime, just sorta hope for the best :V
}
}
2017-10-22 00:06:35 -07:00
else
{
2017-10-22 00:06:35 -07:00
player->realtime = 0;
if (player == &players[consoleplayer])
curlap = 0;
}
2017-10-22 00:06:35 -07:00
}
notrealplayer:
2014-03-15 09:59:03 -07:00
if (!player->mo)
return;
player->mo->colorized = false;
player->mo->color = player->skincolor;
2014-03-15 09:59:03 -07:00
P_CalcHeight(player);
}
//
// P_MoveCamera: make sure the camera is not outside the world and looks at the player avatar
//
camera_t camera[MAXSPLITSCREENPLAYERS]; // Four cameras, three for splitscreen
2014-03-15 09:59:03 -07:00
static void CV_CamRotate_OnChange(void)
{
if (cv_cam_rotate.value < 0)
CV_SetValue(&cv_cam_rotate, cv_cam_rotate.value + 360);
else if (cv_cam_rotate.value > 359)
CV_SetValue(&cv_cam_rotate, cv_cam_rotate.value % 360);
}
static void CV_CamRotate2_OnChange(void)
{
if (cv_cam2_rotate.value < 0)
CV_SetValue(&cv_cam2_rotate, cv_cam2_rotate.value + 360);
else if (cv_cam2_rotate.value > 359)
CV_SetValue(&cv_cam2_rotate, cv_cam2_rotate.value % 360);
}
2017-12-10 22:12:38 -08:00
static void CV_CamRotate3_OnChange(void)
{
if (cv_cam3_rotate.value < 0)
CV_SetValue(&cv_cam3_rotate, cv_cam3_rotate.value + 360);
else if (cv_cam3_rotate.value > 359)
CV_SetValue(&cv_cam3_rotate, cv_cam3_rotate.value % 360);
}
static void CV_CamRotate4_OnChange(void)
{
if (cv_cam4_rotate.value < 0)
CV_SetValue(&cv_cam4_rotate, cv_cam4_rotate.value + 360);
else if (cv_cam4_rotate.value > 359)
CV_SetValue(&cv_cam4_rotate, cv_cam4_rotate.value % 360);
}
2014-11-11 16:55:07 -08:00
static CV_PossibleValue_t CV_CamSpeed[] = {{0, "MIN"}, {1*FRACUNIT, "MAX"}, {0, NULL}};
2014-03-15 09:59:03 -07:00
static CV_PossibleValue_t rotation_cons_t[] = {{1, "MIN"}, {45, "MAX"}, {0, NULL}};
static CV_PossibleValue_t CV_CamRotate[] = {{-720, "MIN"}, {720, "MAX"}, {0, NULL}};
consvar_t cv_cam_dist = {"cam_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cam_height = {"cam_height", "50", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_cam_still = {"cam_still", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
2018-09-04 18:18:20 -07:00
consvar_t cv_cam_speed = {"cam_speed", "0.4", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_cam_rotate = {"cam_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cam_rotspeed = {"cam_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2019-01-05 12:59:23 -08:00
consvar_t cv_cam2_dist = {"cam2_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cam2_height = {"cam2_height", "50", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_cam2_still = {"cam2_still", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
2018-09-04 18:18:20 -07:00
consvar_t cv_cam2_speed = {"cam2_speed", "0.4", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_cam2_rotate = {"cam2_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate2_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cam2_rotspeed = {"cam2_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
2017-12-10 22:12:38 -08:00
consvar_t cv_cam3_dist = {"cam3_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cam3_height = {"cam3_height", "50", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cam3_still = {"cam3_still", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
2018-09-04 18:18:20 -07:00
consvar_t cv_cam3_speed = {"cam3_speed", "0.4", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL, 0, NULL, NULL, 0, 0, NULL};
2017-12-10 22:12:38 -08:00
consvar_t cv_cam3_rotate = {"cam3_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate3_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cam3_rotspeed = {"cam3_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2019-01-05 12:59:23 -08:00
2017-12-10 22:12:38 -08:00
consvar_t cv_cam4_dist = {"cam4_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cam4_height = {"cam4_height", "50", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cam4_still = {"cam4_still", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
2018-09-04 18:18:20 -07:00
consvar_t cv_cam4_speed = {"cam4_speed", "0.4", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL, 0, NULL, NULL, 0, 0, NULL};
2017-12-10 22:12:38 -08:00
consvar_t cv_cam4_rotate = {"cam4_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate4_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cam4_rotspeed = {"cam4_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
fixed_t t_cam_dist = -42;
fixed_t t_cam_height = -42;
fixed_t t_cam_rotate = -42;
fixed_t t_cam2_dist = -42;
fixed_t t_cam2_height = -42;
fixed_t t_cam2_rotate = -42;
fixed_t t_cam3_dist = -42;
fixed_t t_cam3_height = -42;
fixed_t t_cam3_rotate = -42;
fixed_t t_cam4_dist = -42;
fixed_t t_cam4_height = -42;
fixed_t t_cam4_rotate = -42;
2014-03-15 09:59:03 -07:00
#define MAXCAMERADIST 140*FRACUNIT // Max distance the camera can be in front of the player (2D mode)
2019-09-05 14:24:23 -07:00
// Heavily simplified version of G_BuildTicCmd that only takes the local first player's control input and converts it to readable ticcmd_t
// we then throw that ticcmd garbage in the camera and make it move
// redefine this
static fixed_t forwardmove[2] = {25<<FRACBITS>>16, 50<<FRACBITS>>16};
static fixed_t sidemove[2] = {2<<FRACBITS>>16, 4<<FRACBITS>>16};
static fixed_t angleturn[3] = {KART_FULLTURN/2, KART_FULLTURN, KART_FULLTURN/4}; // + slow turn
static ticcmd_t cameracmd;
struct demofreecam_s democam;
// called by m_menu to reinit cam input every time it's toggled
2019-09-05 14:24:23 -07:00
void P_InitCameraCmd(void)
{
memset(&cameracmd, 0, sizeof(ticcmd_t)); // initialize cmd
}
2019-09-05 14:24:23 -07:00
static ticcmd_t *P_CameraCmd(camera_t *cam)
{
INT32 laim, th, tspeed, forward, side, axis; //i
const INT32 speed = 1;
// these ones used for multiple conditions
boolean turnleft, turnright, mouseaiming;
boolean invertmouse, lookaxis, usejoystick, kbl;
angle_t lang;
2020-04-07 17:06:20 -07:00
INT32 player_invert;
INT32 screen_invert;
2019-09-05 14:24:23 -07:00
ticcmd_t *cmd = &cameracmd;
2020-04-07 17:06:20 -07:00
(void)cam;
2019-09-05 14:24:23 -07:00
if (!demo.playback)
return cmd; // empty cmd, no.
2019-09-05 14:24:23 -07:00
lang = democam.localangle;
laim = democam.localaiming;
th = democam.turnheld;
kbl = democam.keyboardlook;
2019-09-05 14:24:23 -07:00
G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver
cmd->angleturn = (INT16)(lang >> 16);
cmd->aiming = G_ClipAimingPitch(&laim);
mouseaiming = true;
invertmouse = cv_invertmouse.value;
lookaxis = cv_lookaxis.value;
usejoystick = true;
turnright = InputDown(gc_turnright, 1);
turnleft = InputDown(gc_turnleft, 1);
axis = JoyAxis(AXISTURN, 1);
if (encoremode)
{
turnright ^= turnleft; // swap these using three XORs
turnleft ^= turnright;
turnright ^= turnleft;
axis = -axis;
}
if (axis != 0)
{
turnright = turnright || (axis > 0);
turnleft = turnleft || (axis < 0);
}
forward = side = 0;
// use two stage accelerative turning
// on the keyboard and joystick
if (turnleft || turnright)
th += 1;
else
th = 0;
if (th < SLOWTURNTICS)
tspeed = 2; // slow turn
else
tspeed = speed;
// let movement keys cancel each other out
if (turnright && !(turnleft))
{
cmd->angleturn = (INT16)(cmd->angleturn - (angleturn[tspeed]));
side += sidemove[1];
}
else if (turnleft && !(turnright))
{
cmd->angleturn = (INT16)(cmd->angleturn + (angleturn[tspeed]));
side -= sidemove[1];
}
2019-09-05 14:24:23 -07:00
cmd->angleturn = (INT16)(cmd->angleturn - ((mousex*(encoremode ? -1 : 1)*8)));
axis = JoyAxis(AXISMOVE, 1);
if (InputDown(gc_accelerate, 1) || (usejoystick && axis > 0))
cmd->buttons |= BT_ACCELERATE;
axis = JoyAxis(AXISBRAKE, 1);
if (InputDown(gc_brake, 1) || (usejoystick && axis > 0))
cmd->buttons |= BT_BRAKE;
axis = JoyAxis(AXISAIM, 1);
if (InputDown(gc_aimforward, 1) || (usejoystick && axis < 0))
forward += forwardmove[1];
if (InputDown(gc_aimbackward, 1) || (usejoystick && axis > 0))
forward -= forwardmove[1];
// fire with any button/key
axis = JoyAxis(AXISFIRE, 1);
if (InputDown(gc_fire, 1) || (usejoystick && axis > 0))
cmd->buttons |= BT_ATTACK;
2019-09-05 14:24:23 -07:00
// spectator aiming shit, ahhhh...
2020-04-07 17:06:20 -07:00
player_invert = invertmouse ? -1 : 1;
screen_invert = 1; // nope
2019-09-05 14:24:23 -07:00
// mouse look stuff (mouse look is not the same as mouse aim)
kbl = false;
// looking up/down
laim += (mlooky<<19)*player_invert*screen_invert;
axis = JoyAxis(AXISLOOK, 1);
// spring back if not using keyboard neither mouselookin'
if (!kbl && !lookaxis && !mouseaiming)
laim = 0;
if (InputDown(gc_lookup, 1) || (axis < 0))
{
laim += KB_LOOKSPEED * screen_invert;
kbl = true;
}
else if (InputDown(gc_lookdown, 1) || (axis > 0))
{
laim -= KB_LOOKSPEED * screen_invert;
kbl = true;
}
if (InputDown(gc_centerview, 1)) // No need to put a spectator limit on this one though :V
laim = 0;
cmd->aiming = G_ClipAimingPitch(&laim);
2019-09-05 14:24:23 -07:00
mousex = mousey = mlooky = 0;
if (forward > MAXPLMOVE)
forward = MAXPLMOVE;
else if (forward < -MAXPLMOVE)
forward = -MAXPLMOVE;
if (side > MAXPLMOVE)
side = MAXPLMOVE;
else if (side < -MAXPLMOVE)
side = -MAXPLMOVE;
if (forward || side)
{
cmd->forwardmove = (SINT8)(cmd->forwardmove + forward);
cmd->sidemove = (SINT8)(cmd->sidemove + side);
}
lang += (cmd->angleturn<<16);
2019-09-05 14:24:23 -07:00
democam.localangle = lang;
democam.localaiming = laim;
democam.turnheld = th;
democam.keyboardlook = kbl;
2019-09-05 14:24:23 -07:00
return cmd;
}
void P_DemoCameraMovement(camera_t *cam)
{
ticcmd_t *cmd;
2019-09-05 14:24:23 -07:00
angle_t thrustangle;
mobj_t *awayviewmobj_hack;
player_t *lastp;
2019-09-05 14:24:23 -07:00
// update democam stuff with what we got here:
democam.cam = cam;
democam.localangle = cam->angle;
democam.localaiming = cam->aiming;
2019-09-05 14:24:23 -07:00
// first off we need to get button input
cmd = P_CameraCmd(cam);
2019-09-05 14:24:23 -07:00
cam->aiming = cmd->aiming<<FRACBITS;
cam->angle = cmd->angleturn<<16;
2019-09-05 14:24:23 -07:00
// camera movement:
if (cmd->buttons & BT_ACCELERATE)
cam->z += 32*mapobjectscale;
else if (cmd->buttons & BT_BRAKE)
cam->z -= 32*mapobjectscale;
// if you hold item, you will lock on to displayplayer. (The last player you were ""f12-ing"")
if (cmd->buttons & BT_ATTACK)
{
lastp = &players[displayplayers[0]]; // Fun fact, I was trying displayplayers[0]->mo as if it was Lua like an absolute idiot.
cam->angle = R_PointToAngle2(cam->x, cam->y, lastp->mo->x, lastp->mo->y);
cam->aiming = R_PointToAngle2(0, cam->z, R_PointToDist2(cam->x, cam->y, lastp->mo->x, lastp->mo->y), lastp->mo->z + lastp->mo->scale*128*P_MobjFlip(lastp->mo)); // This is still unholy. Aim a bit above their heads.
}
2019-09-05 14:24:23 -07:00
cam->momx = cam->momy = cam->momz = 0;
if (cmd->forwardmove != 0)
{
2019-09-05 14:24:23 -07:00
thrustangle = cam->angle >> ANGLETOFINESHIFT;
cam->x += FixedMul(cmd->forwardmove*mapobjectscale, FINECOSINE(thrustangle));
cam->y += FixedMul(cmd->forwardmove*mapobjectscale, FINESINE(thrustangle));
cam->z += FixedMul(cmd->forwardmove*mapobjectscale, AIMINGTOSLOPE(cam->aiming));
// momentums are useless here, directly add to the coordinates
2019-09-05 14:24:23 -07:00
// this.......... doesn't actually check for floors and walls and whatnot but the function to do that is a pure mess so fuck that.
// besides freecam going inside walls sounds pretty cool on paper.
}
// awayviewmobj hack; this is to prevent us from hearing sounds from the player's perspective
awayviewmobj_hack = P_SpawnMobj(cam->x, cam->y, cam->z, MT_THOK);
awayviewmobj_hack->tics = 2;
awayviewmobj_hack->flags2 |= MF2_DONTDRAW;
democam.soundmobj = awayviewmobj_hack;
2019-09-18 05:04:23 -07:00
2019-09-16 15:04:32 -07:00
// update subsector to avoid crashes;
2019-09-18 05:04:23 -07:00
cam->subsector = R_PointInSubsector(cam->x, cam->y);
}
2019-09-05 14:24:23 -07:00
2014-03-15 09:59:03 -07:00
void P_ResetCamera(player_t *player, camera_t *thiscam)
{
2020-04-07 17:06:20 -07:00
tic_t tries = 0;
fixed_t x, y, z;
if (demo.freecam)
return; // do not reset the camera there.
2014-03-15 09:59:03 -07:00
if (!player->mo)
return;
if (thiscam->chase && player->mo->health <= 0)
return;
thiscam->chase = true;
x = player->mo->x - P_ReturnThrustX(player->mo, thiscam->angle, player->mo->radius);
y = player->mo->y - P_ReturnThrustY(player->mo, thiscam->angle, player->mo->radius);
if (player->mo->eflags & MFE_VERTICALFLIP)
z = player->mo->z + player->mo->height - (32<<FRACBITS) - 16*FRACUNIT;
2014-03-15 09:59:03 -07:00
else
z = player->mo->z + (32<<FRACBITS);
2014-03-15 09:59:03 -07:00
// set bits for the camera
thiscam->x = x;
thiscam->y = y;
thiscam->z = z;
if (!(thiscam == &camera[0] && (cv_cam_still.value || cv_analog.value))
&& !(thiscam == &camera[1] && (cv_cam2_still.value || cv_analog2.value))
&& !(thiscam == &camera[2] && (cv_cam3_still.value || cv_analog3.value))
&& !(thiscam == &camera[3] && (cv_cam4_still.value || cv_analog4.value)))
2014-03-15 09:59:03 -07:00
{
thiscam->angle = player->mo->angle;
thiscam->aiming = 0;
}
thiscam->relativex = 0;
thiscam->subsector = R_PointInSubsector(thiscam->x,thiscam->y);
thiscam->radius = 20*FRACUNIT;
thiscam->height = 16*FRACUNIT;
while (!P_MoveChaseCamera(player,thiscam,true) && ++tries < 2*TICRATE);
}
boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled)
{
static UINT8 lookbackdelay[4] = {0,0,0,0};
UINT8 num;
2014-03-15 09:59:03 -07:00
angle_t angle = 0, focusangle = 0, focusaiming = 0;
2018-09-04 13:08:37 -07:00
fixed_t x, y, z, dist, height, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight;
fixed_t pan, xpan, ypan;
2014-03-15 09:59:03 -07:00
INT32 camrotate;
2018-10-22 13:16:57 -07:00
boolean camstill, lookback;
2018-10-23 14:48:09 -07:00
UINT8 timeover;
2014-03-15 09:59:03 -07:00
mobj_t *mo;
fixed_t f1, f2;
2018-10-22 13:16:57 -07:00
#ifndef NOCLIPCAM
boolean cameranoclip;
subsector_t *newsubsec;
#endif
democam.soundmobj = NULL; // reset this each frame, we don't want the game crashing for stupid reasons now do we
// We probably shouldn't move the camera if there is no player or player mobj somehow
if (!player || !player->mo)
return true;
// This can happen when joining
if (thiscam->subsector == NULL || thiscam->subsector->sector == NULL)
return true;
2019-09-16 15:04:32 -07:00
if (demo.freecam)
{
P_DemoCameraMovement(thiscam);
return true;
2019-09-18 05:04:23 -07:00
}
mo = player->mo;
2018-10-22 13:16:57 -07:00
#ifndef NOCLIPCAM
cameranoclip = ((player->pflags & (PF_NOCLIP|PF_NIGHTSMODE))
|| (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)) // Noclipping player camera noclips too!!
|| (leveltime < introtime)); // Kart intro cam
#endif
2014-03-15 09:59:03 -07:00
2018-10-23 14:48:09 -07:00
if (player->pflags & PF_TIMEOVER) // 1 for momentum keep, 2 for turnaround
timeover = (player->kartstuff[k_timeovercam] > 2*TICRATE ? 2 : 1);
else
timeover = 0;
2014-03-15 09:59:03 -07:00
if (!(player->playerstate == PST_DEAD || player->exiting))
2016-07-05 21:09:17 -07:00
{
if (player->spectator) // force cam off for spectators
return true;
2014-03-15 09:59:03 -07:00
if (!cv_chasecam.value && thiscam == &camera[0])
2016-07-05 21:09:17 -07:00
return true;
2014-03-15 09:59:03 -07:00
if (!cv_chasecam2.value && thiscam == &camera[1])
2016-07-05 21:09:17 -07:00
return true;
if (!cv_chasecam3.value && thiscam == &camera[2])
return true;
if (!cv_chasecam4.value && thiscam == &camera[3])
return true;
2016-07-05 21:09:17 -07:00
}
2014-03-15 09:59:03 -07:00
if (!thiscam->chase && !resetcalled)
{
if (player == &players[consoleplayer])
focusangle = localangle[0];
else if (player == &players[displayplayers[1]])
focusangle = localangle[1];
else if (player == &players[displayplayers[2]])
focusangle = localangle[2];
else if (player == &players[displayplayers[3]])
focusangle = localangle[3];
2014-03-15 09:59:03 -07:00
else
focusangle = mo->angle;
if (thiscam == &camera[0])
2014-03-15 09:59:03 -07:00
camrotate = cv_cam_rotate.value;
else if (thiscam == &camera[1])
2014-03-15 09:59:03 -07:00
camrotate = cv_cam2_rotate.value;
else if (thiscam == &camera[2])
camrotate = cv_cam3_rotate.value;
else if (thiscam == &camera[3])
camrotate = cv_cam4_rotate.value;
2014-03-15 09:59:03 -07:00
else
camrotate = 0;
if (leveltime < introtime) // Whoooshy camera!
{
const INT32 introcam = (introtime - leveltime);
camrotate += introcam*5;
}
2014-03-15 09:59:03 -07:00
thiscam->angle = focusangle + FixedAngle(camrotate*FRACUNIT);
P_ResetCamera(player, thiscam);
return true;
}
thiscam->radius = 20*mapobjectscale;
thiscam->height = 16*mapobjectscale;
2014-03-15 09:59:03 -07:00
// Don't run while respawning from a starpost
// Inu 4/8/13 Why not?!
// if (leveltime > 0 && timeinmap <= 0)
// return true;
if (demo.playback)
2014-03-15 09:59:03 -07:00
{
focusangle = mo->angle;
2014-03-15 09:59:03 -07:00
focusaiming = 0;
}
else if (player == &players[consoleplayer])
{
focusangle = localangle[0];
focusaiming = localaiming[0];
2014-03-15 09:59:03 -07:00
}
else if (player == &players[displayplayers[1]])
2014-03-15 09:59:03 -07:00
{
focusangle = localangle[1];
focusaiming = localaiming[1];
2014-03-15 09:59:03 -07:00
}
else if (player == &players[displayplayers[2]])
{
focusangle = localangle[2];
focusaiming = localaiming[2];
}
else if (player == &players[displayplayers[3]])
{
focusangle = localangle[3];
focusaiming = localaiming[3];
}
2014-03-15 09:59:03 -07:00
else
{
focusangle = mo->angle;
2014-03-15 09:59:03 -07:00
focusaiming = player->aiming;
}
if (P_CameraThinker(player, thiscam, resetcalled))
return true;
if (thiscam == &camera[1]) // Camera 2
2014-03-15 09:59:03 -07:00
{
num = 1;
2016-07-05 21:09:17 -07:00
camspeed = cv_cam2_speed.value;
2014-03-15 09:59:03 -07:00
camstill = cv_cam2_still.value;
camrotate = cv_cam2_rotate.value;
camdist = FixedMul(cv_cam2_dist.value, mapobjectscale);
camheight = FixedMul(cv_cam2_height.value, mapobjectscale);
lookback = camspin[1];
2014-03-15 09:59:03 -07:00
}
else if (thiscam == &camera[2]) // Camera 3
2017-12-17 18:23:11 -08:00
{
num = 2;
2017-12-17 18:23:11 -08:00
camspeed = cv_cam3_speed.value;
camstill = cv_cam3_still.value;
camrotate = cv_cam3_rotate.value;
camdist = FixedMul(cv_cam3_dist.value, mapobjectscale);
camheight = FixedMul(cv_cam3_height.value, mapobjectscale);
lookback = camspin[2];
2017-12-17 18:23:11 -08:00
}
else if (thiscam == &camera[3]) // Camera 4
2017-12-17 18:23:11 -08:00
{
num = 3;
2017-12-17 18:23:11 -08:00
camspeed = cv_cam4_speed.value;
camstill = cv_cam4_still.value;
camrotate = cv_cam4_rotate.value;
camdist = FixedMul(cv_cam4_dist.value, mapobjectscale);
camheight = FixedMul(cv_cam4_height.value, mapobjectscale);
lookback = camspin[3];
}
else // Camera 1
{
num = 0;
camspeed = cv_cam_speed.value;
camstill = cv_cam_still.value;
camrotate = cv_cam_rotate.value;
camdist = FixedMul(cv_cam_dist.value, mapobjectscale);
camheight = FixedMul(cv_cam_height.value, mapobjectscale);
lookback = camspin[0];
2017-12-17 18:23:11 -08:00
}
2014-03-15 09:59:03 -07:00
2018-10-23 14:48:09 -07:00
if (timeover)
{
2018-10-23 16:43:20 -07:00
const INT32 timeovercam = max(0, min(180, (player->kartstuff[k_timeovercam] - 2*TICRATE)*15));
2018-10-23 14:48:09 -07:00
camrotate += timeovercam;
}
else if (leveltime < introtime && !(modeattacking && !demo.playback)) // Whoooshy camera! (don't do this in RA when we PLAY, still do it in replays however~)
{
const INT32 introcam = (introtime - leveltime);
camrotate += introcam*5;
camdist += (introcam * mapobjectscale)*3;
camheight += (introcam * mapobjectscale)*2;
}
else if (player->exiting) // SRB2Kart: Leave the camera behind while exiting, for dramatic effect!
camstill = true;
else if (lookback || lookbackdelay[num]) // SRB2kart - Camera flipper
{
camspeed = FRACUNIT;
if (lookback)
{
camrotate += 180;
lookbackdelay[num] = 2;
}
else
lookbackdelay[num]--;
2017-12-17 18:23:11 -08:00
}
2014-03-15 09:59:03 -07:00
if (mo->eflags & MFE_VERTICALFLIP)
camheight += thiscam->height;
if (camspeed > FRACUNIT)
camspeed = FRACUNIT;
2018-10-23 16:43:20 -07:00
if (timeover)
angle = mo->angle + FixedAngle(camrotate*FRACUNIT);
else if (leveltime < starttime)
angle = focusangle + FixedAngle(camrotate*FRACUNIT);
2018-10-23 14:48:09 -07:00
else if (camstill || resetcalled || player->playerstate == PST_DEAD)
angle = thiscam->angle;
2014-03-15 09:59:03 -07:00
else
{
if (camspeed == FRACUNIT)
angle = focusangle + FixedAngle(camrotate<<FRACBITS);
else
{
angle_t input = focusangle + FixedAngle(camrotate<<FRACBITS) - thiscam->angle;
boolean invert = (input > ANGLE_180);
if (invert)
input = InvAngle(input);
input = FixedAngle(FixedMul(AngleFixed(input), camspeed));
if (invert)
input = InvAngle(input);
2014-03-15 09:59:03 -07:00
angle = thiscam->angle + input;
}
}
2018-10-23 14:48:09 -07:00
if (!resetcalled && (leveltime > starttime && timeover != 2)
&& ((thiscam == &camera[0] && t_cam_rotate != -42)
|| (thiscam == &camera[1] && t_cam2_rotate != -42)
|| (thiscam == &camera[2] && t_cam3_rotate != -42)
|| (thiscam == &camera[3] && t_cam4_rotate != -42)))
2014-03-15 09:59:03 -07:00
{
angle = FixedAngle(camrotate*FRACUNIT);
thiscam->angle = angle;
}
height = camheight;
2014-03-15 09:59:03 -07:00
// sets ideal cam pos
dist = camdist;
2014-03-15 09:59:03 -07:00
if (player->speed > K_GetKartSpeed(player, false))
2018-09-05 08:19:00 -07:00
dist += 4*(player->speed - K_GetKartSpeed(player, false));
dist += abs(thiscam->momz)/4;
2018-09-05 08:19:00 -07:00
if (player->kartstuff[k_boostcam])
{
dist -= FixedMul(11*dist/16, player->kartstuff[k_boostcam]);
height -= FixedMul(height, player->kartstuff[k_boostcam]);
}
x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
// SRB2Kart: set camera panning
2018-10-23 16:06:26 -07:00
if (camstill || resetcalled || player->playerstate == PST_DEAD)
2018-10-23 16:08:13 -07:00
pan = xpan = ypan = 0;
2018-10-23 16:06:26 -07:00
else
{
if (player->kartstuff[k_drift] != 0)
{
fixed_t panmax = (dist/5);
pan = FixedDiv(FixedMul(min((fixed_t)player->kartstuff[k_driftcharge], K_GetKartDriftSparkValue(player)), panmax), K_GetKartDriftSparkValue(player));
if (pan > panmax)
pan = panmax;
if (player->kartstuff[k_drift] < 0)
pan *= -1;
}
else
pan = 0;
2018-10-23 16:06:26 -07:00
pan = thiscam->pan + FixedMul(pan - thiscam->pan, camspeed/4);
2018-09-04 13:08:37 -07:00
2018-10-23 16:06:26 -07:00
xpan = FixedMul(FINECOSINE(((angle+ANGLE_90)>>ANGLETOFINESHIFT) & FINEMASK), pan);
ypan = FixedMul(FINESINE(((angle+ANGLE_90)>>ANGLETOFINESHIFT) & FINEMASK), pan);
2018-09-04 13:08:37 -07:00
2018-10-23 16:06:26 -07:00
x += xpan;
y += ypan;
}
2018-09-04 13:08:37 -07:00
pviewheight = FixedMul(32<<FRACBITS, mo->scale);
2014-03-15 09:59:03 -07:00
if (mo->eflags & MFE_VERTICALFLIP)
z = mo->z + mo->height - pviewheight - camheight;
2014-03-15 09:59:03 -07:00
else
z = mo->z + pviewheight + camheight;
2018-10-22 13:16:57 -07:00
#ifndef NOCLIPCAM // Disable all z-clipping for noclip cam
2014-03-15 09:59:03 -07:00
// move camera down to move under lower ceilings
newsubsec = R_IsPointInSubsector(((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1), ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1));
if (!newsubsec)
newsubsec = thiscam->subsector;
if (newsubsec)
{
fixed_t myfloorz, myceilingz;
fixed_t midz = thiscam->z + (thiscam->z - mo->z)/2;
fixed_t midx = ((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1);
fixed_t midy = ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1);
2014-03-15 09:59:03 -07:00
// Cameras use the heightsec's heights rather then the actual sector heights.
// If you can see through it, why not move the camera through it too?
2014-08-03 20:49:33 -07:00
if (newsubsec->sector->camsec >= 0)
{
myfloorz = sectors[newsubsec->sector->camsec].floorheight;
myceilingz = sectors[newsubsec->sector->camsec].ceilingheight;
}
else if (newsubsec->sector->heightsec >= 0)
2014-03-15 09:59:03 -07:00
{
myfloorz = sectors[newsubsec->sector->heightsec].floorheight;
myceilingz = sectors[newsubsec->sector->heightsec].ceilingheight;
}
else
{
myfloorz = P_CameraGetFloorZ(thiscam, newsubsec->sector, midx, midy, NULL);
myceilingz = P_CameraGetCeilingZ(thiscam, newsubsec->sector, midx, midy, NULL);
2014-03-15 09:59:03 -07:00
}
// Check list of fake floors and see if floorz/ceilingz need to be altered.
if (newsubsec->sector->ffloors)
{
ffloor_t *rover;
fixed_t delta1, delta2;
INT32 thingtop = midz + thiscam->height;
for (rover = newsubsec->sector->ffloors; rover; rover = rover->next)
{
fixed_t topheight, bottomheight;
2014-03-15 09:59:03 -07:00
if (!(rover->flags & FF_BLOCKOTHERS) || !(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERALL) || GETSECSPECIAL(rover->master->frontsector->special, 4) == 12)
continue;
topheight = P_CameraGetFOFTopZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
bottomheight = P_CameraGetFOFBottomZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
delta1 = midz - (bottomheight
+ ((topheight - bottomheight)/2));
delta2 = thingtop - (bottomheight
+ ((topheight - bottomheight)/2));
if (topheight > myfloorz && abs(delta1) < abs(delta2))
myfloorz = topheight;
if (bottomheight < myceilingz && abs(delta1) >= abs(delta2))
myceilingz = bottomheight;
2014-03-15 09:59:03 -07:00
}
}
#ifdef POLYOBJECTS
2014-03-21 11:42:55 -07:00
// Check polyobjects and see if floorz/ceilingz need to be altered
2014-03-15 09:59:03 -07:00
{
INT32 xl, xh, yl, yh, bx, by;
validcount++;
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
2016-07-05 21:09:17 -07:00
BMBOUNDFIX(xl, xh, yl, yh);
2014-03-15 09:59:03 -07:00
for (by = yl; by <= yh; by++)
for (bx = xl; bx <= xh; bx++)
{
INT32 offset;
polymaplink_t *plink; // haleyjd 02/22/06
if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight)
continue;
offset = by*bmapwidth + bx;
// haleyjd 02/22/06: consider polyobject lines
plink = polyblocklinks[offset];
while (plink)
{
polyobj_t *po = plink->po;
if (po->validcount != validcount) // if polyobj hasn't been checked
{
sector_t *polysec;
fixed_t delta1, delta2, thingtop;
fixed_t polytop, polybottom;
po->validcount = validcount;
2016-07-05 21:09:17 -07:00
if (!P_PointInsidePolyobj(po, x, y) || !(po->flags & POF_SOLID))
2014-03-15 09:59:03 -07:00
{
plink = (polymaplink_t *)(plink->link.next);
continue;
}
// We're inside it! Yess...
polysec = po->lines[0]->backsector;
if (GETSECSPECIAL(polysec->special, 4) == 12)
{ // Camera noclip polyobj.
plink = (polymaplink_t *)(plink->link.next);
continue;
}
if (po->flags & POF_CLIPPLANES)
{
polytop = polysec->ceilingheight;
polybottom = polysec->floorheight;
}
else
{
polytop = INT32_MAX;
polybottom = INT32_MIN;
}
thingtop = midz + thiscam->height;
delta1 = midz - (polybottom + ((polytop - polybottom)/2));
delta2 = thingtop - (polybottom + ((polytop - polybottom)/2));
2014-03-21 11:42:55 -07:00
if (polytop > myfloorz && abs(delta1) < abs(delta2))
2014-03-15 09:59:03 -07:00
myfloorz = polytop;
2014-03-21 11:42:55 -07:00
if (polybottom < myceilingz && abs(delta1) >= abs(delta2))
2014-03-15 09:59:03 -07:00
myceilingz = polybottom;
}
plink = (polymaplink_t *)(plink->link.next);
}
}
}
#endif
// crushed camera
2014-03-21 11:42:55 -07:00
if (myceilingz <= myfloorz + thiscam->height && !resetcalled && !cameranoclip)
2014-03-15 09:59:03 -07:00
{
P_ResetCamera(player, thiscam);
return true;
}
// camera fit?
if (myceilingz != myfloorz
&& myceilingz - thiscam->height < z)
{
/* // no fit
if (!resetcalled && !cameranoclip)
{
P_ResetCamera(player, thiscam);
return true;
}
*/
z = myceilingz - thiscam->height-FixedMul(11*FRACUNIT, mo->scale);
// is the camera fit is there own sector
}
// Make the camera a tad smarter with 3d floors
if (newsubsec->sector->ffloors && !cameranoclip)
{
ffloor_t *rover;
for (rover = newsubsec->sector->ffloors; rover; rover = rover->next)
{
fixed_t topheight, bottomheight;
if ((rover->flags & FF_BLOCKOTHERS) && (rover->flags & FF_RENDERALL) && (rover->flags & FF_EXISTS) && GETSECSPECIAL(rover->master->frontsector->special, 4) == 12)
2014-03-15 09:59:03 -07:00
{
topheight = P_CameraGetFOFTopZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
bottomheight = P_CameraGetFOFBottomZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
2014-03-15 09:59:03 -07:00
if (bottomheight - thiscam->height < z
&& midz < bottomheight)
z = bottomheight - thiscam->height-FixedMul(11*FRACUNIT, mo->scale);
2014-03-15 09:59:03 -07:00
else if (topheight + thiscam->height > z
&& midz > topheight)
z = topheight;
2014-03-15 09:59:03 -07:00
if ((mo->z >= topheight && midz < bottomheight)
|| ((mo->z < bottomheight && mo->z+mo->height < topheight) && midz >= topheight))
2014-03-15 09:59:03 -07:00
{
// Can't see
if (!resetcalled)
P_ResetCamera(player, thiscam);
return true;
}
}
}
}
}
if (thiscam->z < thiscam->floorz && !cameranoclip)
thiscam->z = thiscam->floorz;
2018-10-22 13:16:57 -07:00
#endif // NOCLIPCAM
2014-03-15 09:59:03 -07:00
// point viewed by the camera
// this point is just 64 unit forward the player
dist = 64*mapobjectscale;
2018-09-04 13:08:37 -07:00
viewpointx = mo->x + FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist) + xpan;
viewpointy = mo->y + FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist) + ypan;
2014-03-15 09:59:03 -07:00
2018-10-23 16:43:20 -07:00
if (timeover)
2018-10-23 14:48:09 -07:00
thiscam->angle = angle;
else if (!camstill && !resetcalled && !paused && timeover != 1)
2014-03-15 09:59:03 -07:00
thiscam->angle = R_PointToAngle2(thiscam->x, thiscam->y, viewpointx, viewpointy);
2018-10-23 14:48:09 -07:00
if (timeover == 1)
2014-03-15 09:59:03 -07:00
{
thiscam->momx = P_ReturnThrustX(NULL, mo->angle, 32*mo->scale); // Push forward
thiscam->momy = P_ReturnThrustY(NULL, mo->angle, 32*mo->scale);
thiscam->momz = 0;
2014-03-15 09:59:03 -07:00
}
2018-10-23 14:48:09 -07:00
else if (player->exiting || timeover == 2)
thiscam->momx = thiscam->momy = thiscam->momz = 0;
else if (leveltime < starttime)
{
2018-09-03 23:51:00 -07:00
thiscam->momx = FixedMul(x - thiscam->x, camspeed);
thiscam->momy = FixedMul(y - thiscam->y, camspeed);
thiscam->momz = FixedMul(z - thiscam->z, camspeed);
}
2014-03-15 09:59:03 -07:00
else
{
2018-09-04 12:11:14 -07:00
thiscam->momx = x - thiscam->x;
thiscam->momy = y - thiscam->y;
thiscam->momz = FixedMul(z - thiscam->z, camspeed/2);
2014-03-15 09:59:03 -07:00
}
2018-09-04 13:08:37 -07:00
thiscam->pan = pan;
2014-03-15 09:59:03 -07:00
// compute aming to look the viewed point
f1 = viewpointx-thiscam->x;
f2 = viewpointy-thiscam->y;
dist = FixedHypot(f1, f2);
if (mo->eflags & MFE_VERTICALFLIP)
angle = R_PointToAngle2(0, thiscam->z + thiscam->height, dist, mo->z + mo->height - P_GetPlayerHeight(player));
else
angle = R_PointToAngle2(0, thiscam->z, dist, mo->z + P_GetPlayerHeight(player));
2018-10-23 14:48:09 -07:00
if (player->playerstate != PST_DEAD && !((player->pflags & PF_NIGHTSMODE) && player->exiting))
2014-03-15 09:59:03 -07:00
angle += (focusaiming < ANGLE_180 ? focusaiming/2 : InvAngle(InvAngle(focusaiming)/2)); // overcomplicated version of '((signed)focusaiming)/2;'
2018-10-23 14:48:09 -07:00
if (twodlevel || (mo->flags2 & MF2_TWOD) || (!camstill && !timeover)) // Keep the view still...
2014-03-15 09:59:03 -07:00
{
G_ClipAimingPitch((INT32 *)&angle);
if (camspeed == FRACUNIT)
thiscam->aiming = angle;
else
{
angle_t input;
boolean invert;
input = thiscam->aiming - angle;
invert = (input > ANGLE_180);
if (invert)
input = InvAngle(input);
input = FixedAngle(FixedMul(AngleFixed(input), (5*camspeed)/16));
if (invert)
input = InvAngle(input);
thiscam->aiming -= input;
}
2014-03-15 09:59:03 -07:00
}
if (!resetcalled && (player->playerstate == PST_DEAD || player->playerstate == PST_REBORN))
2014-03-15 09:59:03 -07:00
{
// Don't let the camera match your movement.
thiscam->momz = 0;
if (player->spectator)
thiscam->aiming = 0;
2014-03-15 09:59:03 -07:00
// Only let the camera go a little bit downwards.
else if (!(mo->eflags & MFE_VERTICALFLIP) && thiscam->aiming < ANGLE_337h && thiscam->aiming > ANGLE_180)
2014-03-15 09:59:03 -07:00
thiscam->aiming = ANGLE_337h;
else if (mo->eflags & MFE_VERTICALFLIP && thiscam->aiming > ANGLE_22h && thiscam->aiming < ANGLE_180)
thiscam->aiming = ANGLE_22h;
}
return (x == thiscam->x && y == thiscam->y && z == thiscam->z && angle == thiscam->aiming);
}
boolean P_SpectatorJoinGame(player_t *player)
2014-08-03 20:49:33 -07:00
{
// Team changing isn't allowed.
if (!cv_allowteamchange.value)
2014-08-03 20:49:33 -07:00
{
if (P_IsLocalPlayer(player))
CONS_Printf(M_GetText("Server does not allow team change.\n"));
//player->powers[pw_flashing] = TICRATE + 1; //to prevent message spam.
2014-08-03 20:49:33 -07:00
}
// Team changing in Team Match and CTF
// Pressing fire assigns you to a team that needs players if allowed.
// Partial code reproduction from p_tick.c autobalance code.
else if (G_GametypeHasTeams())
{
INT32 changeto = 0;
INT32 z, numplayersred = 0, numplayersblue = 0;
//find a team by num players, score, or random if all else fails.
for (z = 0; z < MAXPLAYERS; ++z)
if (playeringame[z])
{
if (players[z].ctfteam == 1)
++numplayersred;
else if (players[z].ctfteam == 2)
++numplayersblue;
}
// for z
if (numplayersblue > numplayersred)
changeto = 1;
else if (numplayersred > numplayersblue)
changeto = 2;
else if (bluescore > redscore)
changeto = 1;
else if (redscore > bluescore)
changeto = 2;
else
2016-07-05 21:09:17 -07:00
changeto = (P_RandomFixed() & 1) + 1;
2014-08-03 20:49:33 -07:00
if (player->mo)
{
P_RemoveMobj(player->mo);
player->mo = NULL;
}
player->spectator = false;
player->pflags &= ~PF_WANTSTOJOIN;
player->kartstuff[k_spectatewait] = 0;
2014-08-03 20:49:33 -07:00
player->ctfteam = changeto;
player->playerstate = PST_REBORN;
//Reset away view
if (P_IsLocalPlayer(player) && displayplayers[0] != consoleplayer)
displayplayers[0] = consoleplayer;
2014-08-03 20:49:33 -07:00
if (changeto == 1)
2016-07-05 21:09:17 -07:00
CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x85', M_GetText("Red team"), '\x80');
2014-08-03 20:49:33 -07:00
else if (changeto == 2)
2016-07-05 21:09:17 -07:00
CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x84', M_GetText("Blue team"), '\x80');
2014-08-03 20:49:33 -07:00
return true; // no more player->mo, cannot continue.
}
// Joining in game from firing.
else
{
if (player->mo)
2014-08-03 20:49:33 -07:00
{
P_RemoveMobj(player->mo);
player->mo = NULL;
}
player->spectator = false;
player->pflags &= ~PF_WANTSTOJOIN;
player->kartstuff[k_spectatewait] = 0;
player->playerstate = PST_REBORN;
2014-08-03 20:49:33 -07:00
//Reset away view
if (P_IsLocalPlayer(player) && displayplayers[0] != consoleplayer)
displayplayers[0] = consoleplayer;
2014-08-03 20:49:33 -07:00
HU_AddChatText(va(M_GetText("\x82*%s entered the game."), player_names[player-players]), false);
return true; // no more player->mo, cannot continue.
2014-08-03 20:49:33 -07:00
}
return false;
}
2014-03-15 09:59:03 -07:00
static void P_CalcPostImg(player_t *player)
{
sector_t *sector = player->mo->subsector->sector;
postimg_t *type = NULL;
2014-03-15 09:59:03 -07:00
INT32 *param;
2014-08-03 20:49:33 -07:00
fixed_t pviewheight;
UINT8 i;
2014-08-03 20:49:33 -07:00
if (player->mo->eflags & MFE_VERTICALFLIP)
pviewheight = player->mo->z + player->mo->height - player->viewheight;
else
pviewheight = player->mo->z + player->viewheight;
if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj))
2014-08-03 20:49:33 -07:00
{
sector = player->awayviewmobj->subsector->sector;
pviewheight = player->awayviewmobj->z + 20*FRACUNIT;
}
2014-03-15 09:59:03 -07:00
for (i = 0; i <= splitscreen; i++)
2014-03-15 09:59:03 -07:00
{
if (player == &players[displayplayers[i]])
{
type = &postimgtype[i];
param = &postimgparam[i];
break;
}
2014-03-15 09:59:03 -07:00
}
// see if we are in heat (no, not THAT kind of heat...)
if (P_FindSpecialLineFromTag(13, sector->tag, -1) != -1)
*type = postimg_heat;
else if (sector->ffloors)
{
ffloor_t *rover;
fixed_t topheight;
fixed_t bottomheight;
2014-03-15 09:59:03 -07:00
for (rover = sector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS))
continue;
#ifdef ESLOPE
topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight;
bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight;
#else
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
#endif
if (pviewheight >= topheight || pviewheight <= bottomheight)
2014-08-03 20:49:33 -07:00
continue;
2014-03-15 09:59:03 -07:00
2014-08-03 20:49:33 -07:00
if (P_FindSpecialLineFromTag(13, rover->master->frontsector->tag, -1) != -1)
*type = postimg_heat;
2014-03-15 09:59:03 -07:00
}
}
// see if we are in water (water trumps heat)
if (sector->ffloors)
{
ffloor_t *rover;
fixed_t topheight;
fixed_t bottomheight;
2014-03-15 09:59:03 -07:00
for (rover = sector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE) || rover->flags & FF_BLOCKPLAYER)
continue;
#ifdef ESLOPE
topheight = *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) : *rover->topheight;
bottomheight = *rover->b_slope ? P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y) : *rover->bottomheight;
#else
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
#endif
if (pviewheight >= topheight || pviewheight <= bottomheight)
2014-08-03 20:49:33 -07:00
continue;
2014-03-15 09:59:03 -07:00
2014-08-03 20:49:33 -07:00
*type = postimg_water;
2014-03-15 09:59:03 -07:00
}
}
if (player->mo->eflags & MFE_VERTICALFLIP)
*type = postimg_flip;
#if 1
(void)param;
#else
// Motion blur
if (player->speed > (35<<FRACBITS))
{
*type = postimg_motion;
*param = (player->speed - 32)/4;
if (*param > 5)
*param = 5;
}
#endif
if (encoremode) // srb2kart
*type = postimg_mirror;
2014-03-15 09:59:03 -07:00
}
2017-10-22 00:06:35 -07:00
/*void P_DoPityCheck(player_t *player)
2014-03-15 09:59:03 -07:00
{
// No pity outside of match or CTF.
if (player->spectator
|| !(gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF))
return;
// Apply pity shield if available.
if ((player->pity >= 3 || player->pity < 0) && player->powers[pw_shield] == SH_NONE)
{
if (player->pity > 0)
S_StartSound(player->mo, mobjinfo[MT_PITYSHIELDICO].seesound);
player->pity = 0;
player->powers[pw_shield] = SH_PITY;
P_SpawnShieldOrb(player);
}
2017-10-22 00:06:35 -07:00
}*/
2014-03-15 09:59:03 -07:00
void P_DoTimeOver(player_t *player)
{
2018-11-07 11:56:36 -08:00
if (netgame && player->health > 0)
CON_LogMessage(va(M_GetText("%s ran out of time.\n"), player_names[player-players]));
player->pflags |= PF_TIMEOVER;
if (P_IsLocalPlayer(player) && !demo.playback)
legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p
if (player->mo)
{
S_StopSound(player->mo);
P_DamageMobj(player->mo, NULL, NULL, 10000);
}
2018-10-23 14:48:09 -07:00
player->lives = 0;
P_EndingMusic(player);
if (!exitcountdown)
exitcountdown = 5*TICRATE;
}
2014-03-15 09:59:03 -07:00
//
// P_PlayerThink
//
void P_PlayerThink(player_t *player)
{
ticcmd_t *cmd;
const size_t playeri = (size_t)(player - players);
#ifdef PARANOIA
if (!player->mo)
I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri));
#endif
// todo: Figure out what is actually causing these problems in the first place...
if ((player->health <= 0 || player->mo->health <= 0) && player->playerstate == PST_LIVE) //you should be DEAD!
{
CONS_Debug(DBG_GAMELOGIC, "P_PlayerThink: Player %s in PST_LIVE with 0 health. (\"Zombie bug\")\n", sizeu1(playeri));
player->playerstate = PST_DEAD;
}
if (player->bot)
{
if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD)
{
if (B_CheckRespawn(player))
player->playerstate = PST_REBORN;
}
2014-03-15 09:59:03 -07:00
if (player->playerstate == PST_REBORN)
return;
}
#ifdef SEENAMES
if (netgame && player == &players[displayplayers[0]] && !(leveltime % (TICRATE/5)) && !splitscreen)
2014-03-15 09:59:03 -07:00
{
seenplayer = NULL;
if (cv_seenames.value && cv_allowseenames.value &&
!(G_TagGametype() && (player->pflags & PF_TAGIT)))
{
mobj_t *mo = P_SpawnNameFinder(player->mo, MT_NAMECHECK);
if (mo)
{
short int i;
mo->flags |= MF_NOCLIPHEIGHT;
for (i = 0; i < 32; i++)
{
// Debug drawing
// if (i&1)
// P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK);
if (P_RailThinker(mo))
break; // mobj was removed (missile hit a wall) or couldn't move
}
}
}
}
#endif
if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj))
{
P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid
player->awayviewtics = 0; // reset to zero
}
2016-08-14 20:51:08 -07:00
/*
2014-03-15 09:59:03 -07:00
if (player->pflags & PF_GLIDING)
{
if (player->panim != PA_ABILITY)
P_SetPlayerMobjState(player->mo, S_PLAY_ABL1);
}
else if ((player->pflags & PF_JUMPED) && !player->powers[pw_super] && player->panim != PA_ROLL && player->charability2 == CA2_SPINDASH)
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
2016-08-14 20:51:08 -07:00
*/
2014-03-15 09:59:03 -07:00
if (player->flashcount)
player->flashcount--;
// Re-fixed by Jimita (11-12-2018)
if (player->awayviewtics)
{
player->awayviewtics--;
if (!player->awayviewtics)
player->awayviewtics = -1;
2018-12-12 07:09:47 -08:00
// The timer might've reached zero, but we'll run the remote view camera anyway by setting it to -1.
}
2014-03-15 09:59:03 -07:00
/// \note do this in the cheat code
if (player->pflags & PF_NOCLIP)
player->mo->flags |= MF_NOCLIP;
else
player->mo->flags &= ~MF_NOCLIP;
cmd = &player->cmd;
//@TODO This fixes a one-tic latency on direction handling, AND makes behavior consistent while paused, but is not BC with 1.0.4. Do this for 1.1!
#if 0
// SRB2kart
// Save the dir the player is holding
// to allow items to be thrown forward or backward.
if (cmd->buttons & BT_FORWARD)
player->kartstuff[k_throwdir] = 1;
else if (cmd->buttons & BT_BACKWARD)
player->kartstuff[k_throwdir] = -1;
else
player->kartstuff[k_throwdir] = 0;
#endif
2014-03-15 09:59:03 -07:00
// Add some extra randomization.
if (cmd->forwardmove)
2016-07-05 21:09:17 -07:00
P_RandomFixed();
2014-03-15 09:59:03 -07:00
#ifdef PARANOIA
if (player->playerstate == PST_REBORN)
I_Error("player %s is in PST_REBORN\n", sizeu1(playeri));
#endif
if (!mapreset)
2014-03-15 09:59:03 -07:00
{
if (G_RaceGametype())
2014-03-15 09:59:03 -07:00
{
INT32 i;
// Check if all the players in the race have finished. If so, end the level.
for (i = 0; i < MAXPLAYERS; i++)
2014-03-15 09:59:03 -07:00
{
if (playeringame[i] && !players[i].spectator)
{
if (!players[i].exiting && players[i].lives > 0)
break;
}
2014-03-15 09:59:03 -07:00
}
2018-09-22 15:59:26 -07:00
if (i == MAXPLAYERS && player->exiting == raceexittime+2) // finished
player->exiting = raceexittime+1;
2014-03-15 09:59:03 -07:00
// If 10 seconds are left on the timer,
// begin the drown music for countdown!
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
// SRB2Kart: despite how perfect this is, it's disabled FOR A REASON
/*if (racecountdown == 11*TICRATE - 1)
{
if (P_IsLocalPlayer(player))
S_ChangeMusicInternal("drown", false);
}*/
2014-03-15 09:59:03 -07:00
// If you've hit the countdown and you haven't made
// it to the exit, you're a goner!
else if (racecountdown == 1 && !player->exiting && !player->spectator && player->lives > 0)
{
P_DoTimeOver(player);
2014-03-15 09:59:03 -07:00
if (player->playerstate == PST_DEAD)
return;
}
2014-03-15 09:59:03 -07:00
}
// If it is set, start subtracting
// Don't allow it to go back to 0
2018-09-22 15:59:26 -07:00
if (player->exiting > 1 && (player->exiting < raceexittime+2 || !G_RaceGametype())) // SRB2kart - "&& player->exiting > 1"
player->exiting--;
2014-03-15 09:59:03 -07:00
if (player->exiting && exitcountdown)
player->exiting = 99; // SRB2kart
2014-03-15 09:59:03 -07:00
if (player->exiting == 2 || exitcountdown == 2)
2014-03-15 09:59:03 -07:00
{
if (cv_playersforexit.value) // Count to be sure everyone's exited
2014-03-15 09:59:03 -07:00
{
INT32 i;
2014-03-15 09:59:03 -07:00
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || players[i].bot)
continue;
if (players[i].lives <= 0)
continue;
2014-03-15 09:59:03 -07:00
if (!players[i].exiting || players[i].exiting > 3)
break;
}
if (i == MAXPLAYERS)
{
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
else
player->exiting = 3;
}
else
2014-03-15 09:59:03 -07:00
{
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
}
}
// check water content, set stuff in mobj
P_MobjCheckWater(player->mo);
#ifndef SECTORSPECIALSAFTERTHINK
2014-11-11 16:55:07 -08:00
#ifdef POLYOBJECTS
if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo))
#endif
2014-03-15 09:59:03 -07:00
player->onconveyor = 0;
// check special sectors : damage & secrets
if (!player->spectator)
P_PlayerInSpecialSector(player);
#endif
2014-03-15 09:59:03 -07:00
if (player->playerstate == PST_DEAD)
{
if (player->spectator)
player->mo->flags2 |= MF2_SHADOW;
else
player->mo->flags2 &= ~MF2_SHADOW;
2014-03-15 09:59:03 -07:00
P_DeathThink(player);
return;
}
// Make sure spectators always have a score and ring count of 0.
if (player->spectator)
{
//player->score = 0;
2014-03-15 09:59:03 -07:00
player->mo->health = 1;
player->health = 1;
}
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
#if 0
2014-03-15 09:59:03 -07:00
if ((netgame || multiplayer) && player->lives <= 0)
{
// In Co-Op, replenish a user's lives if they are depleted.
// of course, this is just a cheap hack, meh...
player->lives = cv_startinglives.value;
}
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
#else
player->lives = 1; // SRB2Kart
#endif
2014-03-15 09:59:03 -07:00
// SRB2kart 010217
if (leveltime < starttime)
player->powers[pw_nocontrol] = 2;
/*
2014-03-15 09:59:03 -07:00
if ((gametype == GT_RACE || gametype == GT_COMPETITION) && leveltime < 4*TICRATE)
{
cmd->buttons &= BT_BRAKE; // Remove all buttons except BT_BRAKE
2014-03-15 09:59:03 -07:00
cmd->forwardmove = 0;
cmd->sidemove = 0;
}
*/
2014-03-15 09:59:03 -07:00
// Synchronizes the "real" amount of time spent in the level.
if (!player->exiting)
{
if (leveltime >= starttime)
{
player->realtime = leveltime - starttime;
if (player == &players[consoleplayer])
{
if (player->spectator || !circuitmap)
curlap = 0;
else
curlap++; // This is too complicated to sync to realtime, just sorta hope for the best :V
}
}
2014-03-15 09:59:03 -07:00
else
{
2017-10-22 00:06:35 -07:00
player->realtime = 0;
if (player == &players[consoleplayer])
curlap = 0;
}
2014-03-15 09:59:03 -07:00
}
if ((netgame || multiplayer) && player->spectator && cmd->buttons & BT_ATTACK && !player->powers[pw_flashing])
2014-08-03 20:49:33 -07:00
{
player->pflags ^= PF_WANTSTOJOIN;
player->powers[pw_flashing] = TICRATE/2 + 1;
/*if (P_SpectatorJoinGame(player))
return; // player->mo was removed.*/
2014-08-03 20:49:33 -07:00
}
// Even if not NiGHTS, pull in nearby objects when walking around as John Q. Elliot.
if (!objectplacing && !((netgame || multiplayer) && player->spectator)
&& maptol & TOL_NIGHTS && (!(player->pflags & PF_NIGHTSMODE) || player->powers[pw_nights_helper]))
{
thinker_t *th;
mobj_t *mo2;
fixed_t x = player->mo->x;
fixed_t y = player->mo->y;
fixed_t z = player->mo->z;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (!(mo2->type == MT_NIGHTSWING || mo2->type == MT_RING || mo2->type == MT_COIN
|| mo2->type == MT_BLUEBALL))
2014-08-03 20:49:33 -07:00
continue;
if (P_AproxDistance(P_AproxDistance(mo2->x - x, mo2->y - y), mo2->z - z) > FixedMul(128*FRACUNIT, player->mo->scale))
continue;
// Yay! The thing's in reach! Pull it in!
mo2->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
mo2->flags2 |= MF2_NIGHTSPULL;
P_SetTarget(&mo2->tracer, player->mo);
}
}
if (player->linktimer && !player->powers[pw_nights_linkfreeze])
{
if (--player->linktimer <= 0) // Link timer
player->linkcount = 0;
}
2014-03-15 09:59:03 -07:00
// Move around.
// Reactiontime is used to prevent movement
// for a bit after a teleport.
if (player->mo->reactiontime)
player->mo->reactiontime--;
else if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT)
{
2016-08-14 20:51:08 -07:00
// SRB2kart - don't need no rope hangin'
//if (player->pflags & PF_ROPEHANG)
//{
// if (!P_AnalogMove(player))
// player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
2014-03-15 09:59:03 -07:00
2016-08-14 20:51:08 -07:00
// ticruned++;
// if ((cmd->angleturn & TICCMD_RECEIVED) == 0)
// ticmiss++;
2014-03-15 09:59:03 -07:00
2016-08-14 20:51:08 -07:00
// P_DoRopeHang(player);
// P_SetPlayerMobjState(player->mo, S_PLAY_CARRY);
// P_DoJumpStuff(player, &player->cmd);
//}
//else
2014-03-15 09:59:03 -07:00
{
P_DoZoomTube(player);
2016-08-14 20:51:08 -07:00
//if (!(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH) // SRB2kart
// P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
2014-03-15 09:59:03 -07:00
}
player->rmomx = player->rmomy = 0; // no actual momentum from your controls
P_ResetScore(player);
}
else
P_MovePlayer(player);
if (!player->mo)
return; // P_MovePlayer removed player->mo.
2016-07-05 21:09:17 -07:00
// Unset statis flags after moving.
// In other words, if you manually set stasis via code,
// it lasts for one tic.
player->pflags &= ~PF_FULLSTASIS;
2014-11-11 16:55:07 -08:00
#ifdef POLYOBJECTS
if (player->onconveyor == 1)
player->cmomy = player->cmomx = 0;
#endif
//P_DoSuperStuff(player);
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
//P_CheckSneakerAndLivesTimer(player);
2014-08-03 20:49:33 -07:00
P_DoBubbleBreath(player); // Spawn Sonic's bubbles
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
//P_CheckUnderwaterAndSpaceTimer(player); // Display the countdown drown numbers!
2014-08-03 20:49:33 -07:00
P_CheckInvincibilityTimer(player); // Spawn Invincibility Sparkles
P_DoPlayerHeadSigns(player); // Spawn Tag/CTF signs over player's head
#if 1
// "Blur" a bit when you have speed shoes and are going fast enough
if ((player->powers[pw_super] || player->powers[pw_sneakers]
|| player->kartstuff[k_driftboost] || player->kartstuff[k_sneakertimer] || player->kartstuff[k_startboost]) && !player->kartstuff[k_invincibilitytimer] // SRB2kart
&& (player->speed + abs(player->mo->momz)) > FixedMul(20*FRACUNIT,player->mo->scale))
2014-08-03 20:49:33 -07:00
{
UINT8 i;
2014-08-03 20:49:33 -07:00
mobj_t *gmobj = P_SpawnGhostMobj(player->mo);
2014-08-03 20:49:33 -07:00
gmobj->fuse = 2;
if (leveltime & 1)
{
gmobj->frame &= ~FF_TRANSMASK;
gmobj->frame |= tr_trans70<<FF_TRANSSHIFT;
}
// Hide the mobj from our sights if we're the displayplayer and chasecam is off.
2014-08-03 20:49:33 -07:00
// Why not just not spawn the mobj? Well, I'd rather only flirt with
// consistency so much...
for (i = 0; i <= splitscreen; i++)
{
if (player == &players[displayplayers[i]] && !camera[i].chase)
{
gmobj->flags2 |= MF2_DONTDRAW;
break;
}
}
2014-08-03 20:49:33 -07:00
}
#endif
2014-03-15 09:59:03 -07:00
// check for use
if (!(player->pflags & PF_NIGHTSMODE))
{
if (cmd->buttons & BT_BRAKE)
2014-08-26 20:56:30 -07:00
player->pflags |= PF_USEDOWN;
2014-03-15 09:59:03 -07:00
else
player->pflags &= ~PF_USEDOWN;
}
else if (player->mo->tracer) // match tracer's position with yours when NiGHTS
{
P_UnsetThingPosition(player->mo->tracer);
player->mo->tracer->x = player->mo->x;
player->mo->tracer->y = player->mo->y;
2014-08-03 20:49:33 -07:00
if (player->mo->eflags & MFE_VERTICALFLIP)
player->mo->tracer->z = player->mo->z + player->mo->height - player->mo->tracer->height;
else
player->mo->tracer->z = player->mo->z;
2014-03-15 09:59:03 -07:00
player->mo->tracer->floorz = player->mo->floorz;
player->mo->tracer->ceilingz = player->mo->ceilingz;
P_SetThingPosition(player->mo->tracer);
}
// Counters, time dependent power ups.
// Time Bonus & Ring Bonus count settings
// Strength counts up to diminish fade.
if (player->powers[pw_sneakers] && player->powers[pw_sneakers] < UINT16_MAX)
player->powers[pw_sneakers]--;
if (player->powers[pw_invulnerability] && player->powers[pw_invulnerability] < UINT16_MAX)
player->powers[pw_invulnerability]--;
if (player->powers[pw_flashing] && player->powers[pw_flashing] < UINT16_MAX &&
2019-05-17 06:06:18 -07:00
(player->spectator || player->powers[pw_flashing] < K_GetKartFlashing(player)))
2014-03-15 09:59:03 -07:00
player->powers[pw_flashing]--;
2019-05-01 21:39:49 -07:00
if (player->powers[pw_tailsfly] && player->powers[pw_tailsfly] < UINT16_MAX /*&& player->charability != CA_SWIM*/ && !(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))) // tails fly counter
2014-03-15 09:59:03 -07:00
player->powers[pw_tailsfly]--;
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
/* // SRB2kart - Can't drown.
2014-08-03 20:49:33 -07:00
if (player->powers[pw_underwater] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL))
2014-03-15 09:59:03 -07:00
{
if (player->powers[pw_underwater] <= 12*TICRATE+1)
P_RestoreMusic(player); //incase they were about to drown
player->powers[pw_underwater] = 0;
}
else if (player->powers[pw_underwater] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer
player->powers[pw_underwater]--;
2014-08-03 20:49:33 -07:00
if (player->powers[pw_spacetime] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL))
2014-03-15 09:59:03 -07:00
player->powers[pw_spacetime] = 0;
else if (player->powers[pw_spacetime] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer
player->powers[pw_spacetime]--;
Mammoth commit! * Deaths in record attack no longer put you into a glitchy singleplayer game-over state that we somehow both kept around and also broke since we branched Kart off of Vanilla.. * Fix non-standard mapscales making the Death Egg respawn octagons dissasemble themselves. * Allow for MULTIPLE TIME EMBLEMS PER MAP, at least in the emblem UI on the timer. It shows all completed emblems plus the uncompleted emblem up to a total of three. * Major tweaks to the First Person HUD. * I know this was your baby, Sal, and some of the changes may prove controversial - so I've put the ones that are likely to cause the most fuss inside an ifndef block, so that you can toggle it as you please with minimal code changes. * Dontdraw-ness, transparency, and colorization match the player's object! * Moves around on the screen with respect to the direction of the player object's motion, to make drifting look nicer! * Flashes the colour of your drift sparks. * Did a WHOLE bunch of things with respect to music. I'm not sure how to describe this, so I'll go through step-by-step. * Countdowns now play the drowning music again. * Removed/disabled extraenous P_RestoreMusics. * Made map-ending music called by its own function, P_EndingMusic(player_t *player). * Made the ending music play on the LAST player crossing the finishing line in splitscreen, rather than first. * Make dead players spinout and clip through the floor, at least until we add the new death anims. * Fix prior pogo spring usage making dead players fall faster. * Make the time over countdown use the kart font when not splitscreen with 3 or 4 players. * Removed a weird bonus HWR_DrawCroppedPatch function signature in the hardware header.
2018-07-16 12:19:30 -07:00
*/
2014-03-15 09:59:03 -07:00
if (player->powers[pw_gravityboots] && player->powers[pw_gravityboots] < UINT16_MAX)
player->powers[pw_gravityboots]--;
if (player->powers[pw_extralife] && player->powers[pw_extralife] < UINT16_MAX)
player->powers[pw_extralife]--;
if (player->powers[pw_nights_linkfreeze] && player->powers[pw_nights_linkfreeze] < UINT16_MAX)
player->powers[pw_nights_linkfreeze]--;
if (player->powers[pw_nights_superloop] && player->powers[pw_nights_superloop] < UINT16_MAX)
player->powers[pw_nights_superloop]--;
if (player->powers[pw_nights_helper] && player->powers[pw_nights_helper] < UINT16_MAX)
player->powers[pw_nights_helper]--;
if (player->powers[pw_nocontrol] & ((1<<15)-1) && player->powers[pw_nocontrol] < UINT16_MAX)
{
if (!(--player->powers[pw_nocontrol]))
player->pflags &= ~PF_SKIDDOWN;
}
2014-03-15 09:59:03 -07:00
else
player->powers[pw_nocontrol] = 0;
//pw_super acts as a timer now
if (player->powers[pw_super])
player->powers[pw_super]++;
if (player->powers[pw_ingoop])
{
if (player->mo->state == &states[S_KART_STND1]) // SRB2kart - was S_PLAY_STND
2014-03-15 09:59:03 -07:00
player->mo->tics = 2;
player->powers[pw_ingoop]--;
}
if (player->bumpertime)
player->bumpertime--;
if (player->skidtime)
player->skidtime--;
if (player->weapondelay)
player->weapondelay--;
if (player->tossdelay)
player->tossdelay--;
if (player->homing)
player->homing--;
if (player->texttimer)
{
--player->texttimer;
if (!player->texttimer && !player->exiting && player->textvar >= 4)
{
player->texttimer = 4*TICRATE;
player->textvar = 2; // GET n RINGS!
if (player->capsule && player->capsule->health != player->capsule->spawnpoint->angle)
player->textvar++; // GET n MORE RINGS!
}
}
if (player->losstime && !player->powers[pw_flashing])
player->losstime--;
// Flash player after being hit.
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
if (!(//player->pflags & PF_NIGHTSMODE ||
player->kartstuff[k_hyudorotimer] // SRB2kart - fixes Hyudoro not flashing when it should.
|| player->kartstuff[k_growshrinktimer] > 0 // Grow doesn't flash either.
|| player->kartstuff[k_respawn] // Respawn timer (for drop dash effect)
2018-10-23 14:48:09 -07:00
|| (player->pflags & PF_TIMEOVER) // NO CONTEST explosion
|| (G_BattleGametype() && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer])
|| leveltime < starttime)) // Level intro
{
if (player->powers[pw_flashing] > 0 && player->powers[pw_flashing] < K_GetKartFlashing(player)
&& (leveltime & 1))
player->mo->flags2 |= MF2_DONTDRAW;
else
player->mo->flags2 &= ~MF2_DONTDRAW;
2014-03-15 09:59:03 -07:00
}
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
/*else if (player->mo->tracer)
2014-03-15 09:59:03 -07:00
{
if (player->powers[pw_flashing] & 1)
player->mo->tracer->flags2 |= MF2_DONTDRAW;
else
player->mo->tracer->flags2 &= ~MF2_DONTDRAW;
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
}*/
2014-03-15 09:59:03 -07:00
player->pflags &= ~PF_SLIDING;
2016-08-14 20:51:08 -07:00
K_KartPlayerThink(player, cmd); // SRB2kart
2014-03-15 09:59:03 -07:00
/*
// Colormap verification
{
INT32 i,j;
sector_t *controlsec;
for (j=0; j<numsectors; j++)
{
controlsec = NULL;
// Does this sector have a water linedef?
for (i=0; i<numlines;i++)
{
if ((lines[i].special == 121 || lines[i].special == 123)
&& lines[i].tag == sectors[j].tag)
{
controlsec = lines[i].frontsector;
break;
}
}
if (i < numlines && controlsec)
{
// Does this sector have a colormap?
for (i=0; i<numlines;i++)
{
if (lines[i].special == 606 && lines[i].tag == controlsec->tag)
break;
}
if (i == numlines)
CONS_Debug(DBG_GAMELOGIC, "%d, %d\n", j, sectors[j].tag);
}
}
I_Error("I'm done!\n");
}*/
}
//
// P_PlayerAfterThink
//
// Thinker for player after all other thinkers have run
//
void P_PlayerAfterThink(player_t *player)
{
ticcmd_t *cmd;
//INT32 oldweapon = player->currentweapon; // SRB2kart - unused
2016-07-05 21:09:17 -07:00
camera_t *thiscam = NULL; // if not one of the displayed players, just don't bother
UINT8 i;
2014-03-15 09:59:03 -07:00
#ifdef PARANOIA
if (!player->mo)
{
const size_t playeri = (size_t)(player - players);
I_Error("P_PlayerAfterThink: players[%s].mo == NULL", sizeu1(playeri));
}
#endif
cmd = &player->cmd;
#ifdef SECTORSPECIALSAFTERTHINK
#ifdef POLYOBJECTS
if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo))
#endif
player->onconveyor = 0;
// check special sectors : damage & secrets
if (!player->spectator)
P_PlayerInSpecialSector(player);
#endif
for (i = 0; i <= splitscreen; i++)
{
if (player == &players[displayplayers[i]])
{
thiscam = &camera[i];
break;
}
}
2014-03-15 09:59:03 -07:00
if (player->playerstate == PST_DEAD)
{
return;
}
if (player->pflags & PF_NIGHTSMODE)
{
player->powers[pw_gravityboots] = 0;
2014-08-03 20:49:33 -07:00
//player->mo->eflags &= ~MFE_VERTICALFLIP;
2014-03-15 09:59:03 -07:00
}
/* SRB2kart - don't need no weapon rings
2014-03-15 09:59:03 -07:00
if (!(player->pflags & PF_WPNDOWN))
{
if (cmd->buttons & BT_WEAPONNEXT)
{
player->currentweapon++;
player->currentweapon %= NUM_WEAPONS;
player->pflags |= PF_WPNDOWN;
}
if (cmd->buttons & BT_WEAPONPREV)
{
player->currentweapon--;
if (player->currentweapon < 0)
player->currentweapon = NUM_WEAPONS - 1;
player->pflags |= PF_WPNDOWN;
if (player->currentweapon == WEP_RAIL && (!(player->ringweapons & RW_RAIL) || !player->powers[pw_railring]))
player->currentweapon--;
if (player->currentweapon == WEP_EXPLODE && (!(player->ringweapons & RW_EXPLODE) || !player->powers[pw_explosionring]))
player->currentweapon--;
if (player->currentweapon == WEP_GRENADE && (!(player->ringweapons & RW_GRENADE) || !player->powers[pw_grenadering]))
player->currentweapon--;
if (player->currentweapon == WEP_SCATTER && (!(player->ringweapons & RW_SCATTER) || !player->powers[pw_scatterring]))
player->currentweapon--;
if (player->currentweapon == WEP_BOUNCE && (!(player->ringweapons & RW_BOUNCE) || !player->powers[pw_bouncering]))
player->currentweapon--;
if (player->currentweapon == WEP_AUTO && (!(player->ringweapons & RW_AUTO) || !player->powers[pw_automaticring]))
player->currentweapon = 0;
}
if (cmd->buttons & BT_WEAPONMASK)
{
//Read the bits to determine individual weapon ring selection.
INT32 weapon = (cmd->buttons & BT_WEAPONMASK);
switch (weapon)
{
case 1: //normal / infinity
player->currentweapon = 0;
player->pflags |= PF_WPNDOWN;
break;
case 2: //automatic
if ((player->ringweapons & RW_AUTO) && player->powers[pw_automaticring])
{
player->currentweapon = WEP_AUTO;
player->pflags |= PF_WPNDOWN;
}
break;
case 3: //bounce
if ((player->ringweapons & RW_BOUNCE) && player->powers[pw_bouncering])
{
player->currentweapon = WEP_BOUNCE;
player->pflags |= PF_WPNDOWN;
}
break;
case 4: //scatter
if ((player->ringweapons & RW_SCATTER) && player->powers[pw_scatterring])
{
player->currentweapon = WEP_SCATTER;
player->pflags |= PF_WPNDOWN;
}
break;
case 5: //grenade
if ((player->ringweapons & RW_GRENADE) && player->powers[pw_grenadering])
{
player->currentweapon = WEP_GRENADE;
player->pflags |= PF_WPNDOWN;
}
break;
case 6: //explosion
if ((player->ringweapons & RW_EXPLODE) && player->powers[pw_explosionring])
{
player->currentweapon = WEP_EXPLODE;
player->pflags |= PF_WPNDOWN;
}
break;
case 7: //rail
if ((player->ringweapons & RW_RAIL) && player->powers[pw_railring])
{
player->currentweapon = WEP_RAIL;
player->pflags |= PF_WPNDOWN;
}
break;
}
}
}
2014-08-26 20:56:30 -07:00
if (!(cmd->buttons & (BT_WEAPONNEXT|BT_WEAPONPREV|BT_WEAPONMASK)))
2014-03-15 09:59:03 -07:00
player->pflags &= ~PF_WPNDOWN;
// Weapon cycling if out of ammo for a certain weapon
if (player->currentweapon == WEP_AUTO && (!(player->ringweapons & RW_AUTO) || !player->powers[pw_automaticring]))
player->currentweapon++;
if (player->currentweapon == WEP_BOUNCE && (!(player->ringweapons & RW_BOUNCE) || !player->powers[pw_bouncering]))
player->currentweapon++;
if (player->currentweapon == WEP_SCATTER && (!(player->ringweapons & RW_SCATTER) || !player->powers[pw_scatterring]))
player->currentweapon++;
if (player->currentweapon == WEP_GRENADE && (!(player->ringweapons & RW_GRENADE) || !player->powers[pw_grenadering]))
player->currentweapon++;
if (player->currentweapon == WEP_EXPLODE && (!(player->ringweapons & RW_EXPLODE) || !player->powers[pw_explosionring]))
player->currentweapon++;
if (player->currentweapon == WEP_RAIL && (!(player->ringweapons & RW_RAIL) || !player->powers[pw_railring]))
player->currentweapon = 0;
// If you're out of rings, but have Infinity Rings left, switch to that.
if (player->currentweapon != 0 && player->health <= 1 && player->powers[pw_infinityring])
player->currentweapon = 0;
if (P_IsLocalPlayer(player) && (player->pflags & PF_WPNDOWN) && player->currentweapon != oldweapon)
S_StartSound(NULL, sfx_wepchg);
*/
2014-03-15 09:59:03 -07:00
2016-08-14 20:51:08 -07:00
/* // SRB2kart
2014-03-15 09:59:03 -07:00
if (player->pflags & PF_GLIDING)
{
if (player->panim != PA_ABILITY)
P_SetPlayerMobjState(player->mo, S_PLAY_ABL1);
}
2016-08-14 20:51:08 -07:00
else */
if (player->pflags & PF_SLIDING)
2014-03-15 09:59:03 -07:00
P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
2016-08-14 20:51:08 -07:00
/*else if (player->pflags & PF_JUMPED
2014-03-15 09:59:03 -07:00
&& ((!player->powers[pw_super] && player->panim != PA_ROLL)
|| player->mo->state == &states[player->mo->info->painstate])
&& player->charability2 == CA2_SPINDASH)
2016-08-14 20:51:08 -07:00
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);*/
2014-03-15 09:59:03 -07:00
2016-08-14 20:51:08 -07:00
/* // SRB2kart - fight! fight! fight!
2014-08-26 20:56:30 -07:00
if (player->pflags & PF_CARRIED && player->mo->tracer)
2014-03-15 09:59:03 -07:00
{
player->mo->height = FixedDiv(P_GetPlayerHeight(player), FixedDiv(14*FRACUNIT,10*FRACUNIT));
// State check for the carrier - Flame
// You are an IDIOT, those aren't even the right frames! >_> - JTE
if (player->mo->tracer->player
&& !(player->mo->tracer->state >= &states[S_PLAY_ABL1]
&& player->mo->tracer->state <= &states[S_PLAY_SPC4]))
player->pflags &= ~PF_CARRIED;
if (player->mo->eflags & MFE_VERTICALFLIP)
{
if ((player->mo->tracer->z + player->mo->tracer->height + player->mo->height + FixedMul(FRACUNIT, player->mo->scale)) <= player->mo->tracer->ceilingz
&& (player->mo->tracer->eflags & MFE_VERTICALFLIP)) // Reverse gravity check for the carrier - Flame
player->mo->z = player->mo->tracer->z + player->mo->tracer->height + FixedMul(FRACUNIT, player->mo->scale);
else
player->pflags &= ~PF_CARRIED;
}
else
{
if ((player->mo->tracer->z - player->mo->height - FixedMul(FRACUNIT, player->mo->scale)) >= player->mo->tracer->floorz
&& !(player->mo->tracer->eflags & MFE_VERTICALFLIP)) // Correct gravity check for the carrier - Flame
player->mo->z = player->mo->tracer->z - player->mo->height - FixedMul(FRACUNIT, player->mo->scale);
else
player->pflags &= ~PF_CARRIED;
}
2014-03-18 10:56:54 -07:00
if (player->mo->tracer->health <= 0)
2014-03-15 09:59:03 -07:00
player->pflags &= ~PF_CARRIED;
else
{
P_TryMove(player->mo, player->mo->tracer->x, player->mo->tracer->y, true);
player->mo->momx = player->mo->tracer->momx;
player->mo->momy = player->mo->tracer->momy;
player->mo->momz = player->mo->tracer->momz;
}
if (gametype == GT_COOP)
{
player->mo->angle = player->mo->tracer->angle;
if (player == &players[consoleplayer])
localangle[0] = player->mo->angle;
else if (player == &players[displayplayers[1]])
localangle[1] = player->mo->angle;
else if (player == &players[displayplayers[2]])
localangle[2] = player->mo->angle;
else if (player == &players[displayplayers[3]])
localangle[3] = player->mo->angle;
2014-03-15 09:59:03 -07:00
}
if (P_AproxDistance(player->mo->x - player->mo->tracer->x, player->mo->y - player->mo->tracer->y) > player->mo->radius)
player->pflags &= ~PF_CARRIED;
P_SetPlayerMobjState(player->mo, S_PLAY_CARRY);
//if (player-players == consoleplayer && botingame)
//CV_SetValue(&cv_analog2, !(player->pflags & PF_CARRIED));
2014-03-15 09:59:03 -07:00
}
else if (player->pflags & PF_ITEMHANG && player->mo->tracer)
{
// tracer is what you're hanging onto
P_UnsetThingPosition(player->mo);
player->mo->x = player->mo->tracer->x;
player->mo->y = player->mo->tracer->y;
if (player->mo->eflags & MFE_VERTICALFLIP)
player->mo->z = player->mo->tracer->z + player->mo->tracer->height - FixedDiv(player->mo->height, 3*FRACUNIT);
else
player->mo->z = player->mo->tracer->z - FixedDiv(player->mo->height, 3*FRACUNIT/2);
player->mo->momx = player->mo->momy = player->mo->momz = 0;
P_SetThingPosition(player->mo);
P_SetPlayerMobjState(player->mo, S_PLAY_CARRY);
// Controllable missile
if (player->mo->tracer->type == MT_BLACKEGGMAN_MISSILE)
{
if (cmd->forwardmove > 0)
player->mo->tracer->momz += FixedMul(FRACUNIT/4, player->mo->tracer->scale);
else if (cmd->forwardmove < 0)
player->mo->tracer->momz -= FixedMul(FRACUNIT/4, player->mo->tracer->scale);
player->mo->tracer->angle = player->mo->angle;
P_InstaThrust(player->mo->tracer, player->mo->tracer->angle, FixedMul(player->mo->tracer->info->speed, player->mo->tracer->scale));
if (player->mo->z <= player->mo->floorz
|| player->mo->tracer->health <= 0)
{
player->pflags &= ~PF_ITEMHANG;
P_SetTarget(&player->mo->tracer, NULL);
}
}
}
2016-08-14 20:51:08 -07:00
else */if (player->pflags & PF_MACESPIN && player->mo->tracer && player->mo->tracer->target)
2014-03-15 09:59:03 -07:00
{
player->mo->height = P_GetPlayerSpinHeight(player);
// tracer is what you're hanging onto....
player->mo->momx = (player->mo->tracer->x - player->mo->x)*2;
player->mo->momy = (player->mo->tracer->y - player->mo->y)*2;
player->mo->momz = (player->mo->tracer->z - (player->mo->height-player->mo->tracer->height/2) - player->mo->z)*2;
P_TeleportMove(player->mo, player->mo->tracer->x, player->mo->tracer->y, player->mo->tracer->z - (player->mo->height-player->mo->tracer->height/2));
player->pflags |= PF_JUMPED;
player->secondjump = 0;
player->pflags &= ~PF_THOKKED;
if (cmd->forwardmove > 0)
player->mo->tracer->target->lastlook += 2;
else if (cmd->forwardmove < 0 && player->mo->tracer->target->lastlook > player->mo->tracer->target->movecount)
player->mo->tracer->target->lastlook -= 2;
if (!(player->mo->tracer->target->flags & MF_SLIDEME) // Noclimb on chain parameters gives this
&& !(twodlevel || player->mo->flags2 & MF2_TWOD)) // why on earth would you want to turn them in 2D mode?
{
player->mo->tracer->target->health += cmd->sidemove;
player->mo->angle += cmd->sidemove<<ANGLETOFINESHIFT; // 2048 --> ANGLE_MAX
if (player == &players[consoleplayer])
localangle[0] = player->mo->angle; // Adjust the local control angle.
else if (player == &players[displayplayers[1]])
localangle[1] = player->mo->angle;
else if (player == &players[displayplayers[2]])
localangle[2] = player->mo->angle;
else if (player == &players[displayplayers[3]])
localangle[3] = player->mo->angle;
2014-03-15 09:59:03 -07:00
}
}
2016-07-05 21:09:17 -07:00
if (thiscam)
2014-03-15 09:59:03 -07:00
{
2014-08-26 20:56:30 -07:00
if (!thiscam->chase) // bob view only if looking through the player's eyes
2014-03-15 09:59:03 -07:00
{
P_CalcHeight(player);
P_CalcPostImg(player);
}
else
{
// defaults to make sure 1st person cam doesn't do anything weird on startup
//player->deltaviewheight = 0;
player->viewheight = FixedMul(32 << FRACBITS, player->mo->scale);
2014-03-15 09:59:03 -07:00
if (player->mo->eflags & MFE_VERTICALFLIP)
player->viewz = player->mo->z + player->mo->height - player->viewheight;
else
player->viewz = player->mo->z + player->viewheight;
}
}
if (player->awayviewtics < 0)
player->awayviewtics = 0;
2018-11-05 14:40:51 -08:00
2014-03-15 09:59:03 -07:00
// spectator invisibility and nogravity.
if ((netgame || multiplayer) && player->spectator)
{
player->mo->flags2 |= MF2_DONTDRAW;
player->mo->flags |= MF_NOGRAVITY;
}
2016-07-05 21:09:17 -07:00
if (P_IsObjectOnGround(player->mo))
player->mo->pmomz = 0;
K_KartPlayerAfterThink(player);
2014-03-15 09:59:03 -07:00
}