mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
First pass on Special Stage UFO
- UFO spawns at first waypoint for TOL_SPECIAL maps - UFO moves up the waypoints, the speed varying based on your position relative to it and your own speed. - You are able to tether off of the UFO. - Bugfix: PathfindThruCircuit no longer fails when reaching a one-ended waypoint. Damage is my next project but I wanted to get this committed for now.
This commit is contained in:
parent
df35a72c58
commit
8ca3d41696
11 changed files with 630 additions and 110 deletions
|
|
@ -5617,6 +5617,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
|||
"MT_PAPERITEMSPOT",
|
||||
|
||||
"MT_BEAMPOINT",
|
||||
|
||||
"MT_SPECIAL_UFO",
|
||||
};
|
||||
|
||||
const char *const MOBJFLAG_LIST[] = {
|
||||
|
|
|
|||
27
src/info.c
27
src/info.c
|
|
@ -28990,6 +28990,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_SPECIAL_UFO
|
||||
-1, // doomednum
|
||||
S_CHAOSEMERALD1, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
8, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
72*FRACUNIT, // radius
|
||||
72*FRACUNIT, // height
|
||||
0, // display offset
|
||||
16, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
};
|
||||
|
||||
skincolor_t skincolors[MAXSKINCOLORS] = {
|
||||
|
|
|
|||
|
|
@ -6667,6 +6667,8 @@ typedef enum mobj_type
|
|||
|
||||
MT_BEAMPOINT,
|
||||
|
||||
MT_SPECIAL_UFO,
|
||||
|
||||
MT_FIRSTFREESLOT,
|
||||
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
|
||||
NUMMOBJTYPES
|
||||
|
|
|
|||
254
src/k_kart.c
254
src/k_kart.c
|
|
@ -183,6 +183,9 @@ UINT32 K_GetPlayerDontDrawFlag(player_t *player)
|
|||
{
|
||||
UINT32 flag = 0;
|
||||
|
||||
if (player == NULL)
|
||||
return flag;
|
||||
|
||||
if (player == &players[displayplayers[0]])
|
||||
flag = RF_DONTDRAWP1;
|
||||
else if (r_splitscreen >= 1 && player == &players[displayplayers[1]])
|
||||
|
|
@ -1979,7 +1982,7 @@ static void K_UpdateOffroad(player_t *player)
|
|||
player->offroad = 0;
|
||||
}
|
||||
|
||||
static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t curdist, fixed_t maxdist, boolean transparent)
|
||||
static void K_DrawDraftCombiring(player_t *player, mobj_t *victim, fixed_t curdist, fixed_t maxdist, boolean transparent)
|
||||
{
|
||||
#define CHAOTIXBANDLEN 15
|
||||
#define CHAOTIXBANDCOLORS 9
|
||||
|
|
@ -2010,9 +2013,9 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur
|
|||
c = FixedMul(CHAOTIXBANDCOLORS<<FRACBITS, FixedDiv(curdist-minimumdist, maxdist-minimumdist)) >> FRACBITS;
|
||||
}
|
||||
|
||||
stepx = (victim->mo->x - player->mo->x) / CHAOTIXBANDLEN;
|
||||
stepy = (victim->mo->y - player->mo->y) / CHAOTIXBANDLEN;
|
||||
stepz = ((victim->mo->z + (victim->mo->height / 2)) - (player->mo->z + (player->mo->height / 2))) / CHAOTIXBANDLEN;
|
||||
stepx = (victim->x - player->mo->x) / CHAOTIXBANDLEN;
|
||||
stepy = (victim->y - player->mo->y) / CHAOTIXBANDLEN;
|
||||
stepz = ((victim->z + (victim->height / 2)) - (player->mo->z + (player->mo->height / 2))) / CHAOTIXBANDLEN;
|
||||
|
||||
curx = player->mo->x + stepx;
|
||||
cury = player->mo->y + stepy;
|
||||
|
|
@ -2046,7 +2049,7 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur
|
|||
if (transparent)
|
||||
band->renderflags |= RF_GHOSTLY;
|
||||
|
||||
band->renderflags |= RF_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim));
|
||||
band->renderflags |= RF_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim->player));
|
||||
}
|
||||
|
||||
curx += stepx;
|
||||
|
|
@ -2071,6 +2074,129 @@ static boolean K_HasInfiniteTether(player_t *player)
|
|||
return false;
|
||||
}
|
||||
|
||||
static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed_t draftdistance, UINT8 leniency)
|
||||
{
|
||||
//#define EASYDRAFTTEST
|
||||
fixed_t dist, olddraft;
|
||||
fixed_t theirSpeed = 0;
|
||||
#ifndef EASYDRAFTTEST
|
||||
angle_t yourangle, theirangle, diff;
|
||||
#endif
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
// Don't draft on yourself :V
|
||||
if (dest->player && dest->player == player)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dest->player != NULL)
|
||||
{
|
||||
// No tethering off of the guy who got the starting bonus :P
|
||||
if (dest->player->startboost > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
theirSpeed = dest->player->speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
theirSpeed = R_PointToDist2(0, 0, dest->momx, dest->momy);
|
||||
}
|
||||
|
||||
// They're not enough speed to draft off of them.
|
||||
if (theirSpeed < 20 * dest->scale)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
yourangle = K_MomentumAngle(player->mo);
|
||||
theirangle = K_MomentumAngle(dest);
|
||||
|
||||
// Not in front of this player.
|
||||
diff = AngleDelta(R_PointToAngle2(player->mo->x, player->mo->y, dest->x, dest->y), yourangle);
|
||||
if (diff > ANG10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not moving in the same direction.
|
||||
diff = AngleDelta(yourangle, theirangle);
|
||||
if (diff > ANGLE_90)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
dist = P_AproxDistance(P_AproxDistance(dest->x - player->mo->x, dest->y - player->mo->y), dest->z - player->mo->z);
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
// TOO close to draft.
|
||||
if (dist < minDist)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not close enough to draft.
|
||||
if (dist > draftdistance && draftdistance > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
olddraft = player->draftpower;
|
||||
|
||||
player->draftleeway = leniency;
|
||||
|
||||
if (dest->player != NULL)
|
||||
{
|
||||
player->lastdraft = dest->player - players;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->lastdraft = MAXPLAYERS;
|
||||
}
|
||||
|
||||
// Draft power is used later in K_GetKartBoostPower, ranging from 0 for normal speed and FRACUNIT for max draft speed.
|
||||
// How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic)
|
||||
if (player->draftpower < FRACUNIT)
|
||||
{
|
||||
fixed_t add = (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600));;
|
||||
player->draftpower += add;
|
||||
|
||||
if (player->bot && player->botvars.rival)
|
||||
{
|
||||
// Double speed for the rival!
|
||||
player->draftpower += add;
|
||||
}
|
||||
|
||||
if (gametype == GT_BATTLE)
|
||||
{
|
||||
// TODO: gametyperules
|
||||
// Double speed in Battle
|
||||
player->draftpower += add;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->draftpower > FRACUNIT)
|
||||
{
|
||||
player->draftpower = FRACUNIT;
|
||||
}
|
||||
|
||||
// Play draft finish noise
|
||||
if (olddraft < FRACUNIT && player->draftpower >= FRACUNIT)
|
||||
{
|
||||
S_StartSound(player->mo, sfx_cdfm62);
|
||||
}
|
||||
|
||||
// Spawn in the visual!
|
||||
K_DrawDraftCombiring(player, dest, dist, draftdistance, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \brief Updates the player's drafting values once per frame
|
||||
|
||||
\param player player object passed from K_KartPlayerThink
|
||||
|
|
@ -2079,6 +2205,9 @@ static boolean K_HasInfiniteTether(player_t *player)
|
|||
*/
|
||||
static void K_UpdateDraft(player_t *player)
|
||||
{
|
||||
const boolean addUfo = ((specialStage.active == true)
|
||||
&& (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false));
|
||||
|
||||
fixed_t topspd = K_GetKartSpeed(player, false, false);
|
||||
fixed_t draftdistance;
|
||||
fixed_t minDist;
|
||||
|
|
@ -2118,104 +2247,43 @@ static void K_UpdateDraft(player_t *player)
|
|||
// Not enough speed to draft.
|
||||
if (player->speed >= 20 * player->mo->scale)
|
||||
{
|
||||
//#define EASYDRAFTTEST
|
||||
if (addUfo == true)
|
||||
{
|
||||
// Tether off of the UFO!
|
||||
if (K_TryDraft(player, specialStage.ufo, minDist, draftdistance, leniency) == true)
|
||||
{
|
||||
return; // Finished doing our draft.
|
||||
}
|
||||
}
|
||||
|
||||
// Let's hunt for players to draft off of!
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
fixed_t dist, olddraft;
|
||||
#ifndef EASYDRAFTTEST
|
||||
angle_t yourangle, theirangle, diff;
|
||||
#endif
|
||||
player_t *otherPlayer = NULL;
|
||||
|
||||
if (!playeringame[i] || players[i].spectator || !players[i].mo)
|
||||
continue;
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
// Don't draft on yourself :V
|
||||
if (&players[i] == player)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// Not enough speed to draft off of.
|
||||
if (players[i].speed < 20*players[i].mo->scale)
|
||||
continue;
|
||||
|
||||
// No tethering off of the guy who got the starting bonus :P
|
||||
if (players[i].startboost > 0)
|
||||
continue;
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
yourangle = K_MomentumAngle(player->mo);
|
||||
theirangle = K_MomentumAngle(players[i].mo);
|
||||
|
||||
diff = R_PointToAngle2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y) - yourangle;
|
||||
if (diff > ANGLE_180)
|
||||
diff = InvAngle(diff);
|
||||
|
||||
// Not in front of this player.
|
||||
if (diff > ANG10)
|
||||
continue;
|
||||
|
||||
diff = yourangle - theirangle;
|
||||
if (diff > ANGLE_180)
|
||||
diff = InvAngle(diff);
|
||||
|
||||
// Not moving in the same direction.
|
||||
if (diff > ANGLE_90)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
dist = P_AproxDistance(P_AproxDistance(players[i].mo->x - player->mo->x, players[i].mo->y - player->mo->y), players[i].mo->z - player->mo->z);
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
// TOO close to draft.
|
||||
if (dist < minDist)
|
||||
continue;
|
||||
|
||||
// Not close enough to draft.
|
||||
if (dist > draftdistance && draftdistance > 0)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
olddraft = player->draftpower;
|
||||
|
||||
player->draftleeway = leniency;
|
||||
player->lastdraft = i;
|
||||
|
||||
// Draft power is used later in K_GetKartBoostPower, ranging from 0 for normal speed and FRACUNIT for max draft speed.
|
||||
// How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic)
|
||||
if (player->draftpower < FRACUNIT)
|
||||
if (playeringame[i] == false)
|
||||
{
|
||||
fixed_t add = (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600));;
|
||||
player->draftpower += add;
|
||||
|
||||
if (player->bot && player->botvars.rival)
|
||||
{
|
||||
// Double speed for the rival!
|
||||
player->draftpower += add;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gametype == GT_BATTLE)
|
||||
otherPlayer = &players[i];
|
||||
|
||||
if (otherPlayer->spectator == true)
|
||||
{
|
||||
// TODO: gametyperules
|
||||
// Double speed in Battle
|
||||
player->draftpower += add;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->draftpower > FRACUNIT)
|
||||
player->draftpower = FRACUNIT;
|
||||
|
||||
// Play draft finish noise
|
||||
if (olddraft < FRACUNIT && player->draftpower >= FRACUNIT)
|
||||
S_StartSound(player->mo, sfx_cdfm62);
|
||||
|
||||
// Spawn in the visual!
|
||||
K_DrawDraftCombiring(player, &players[i], dist, draftdistance, false);
|
||||
if (otherPlayer->mo == NULL || P_MobjWasRemoved(otherPlayer->mo) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (K_TryDraft(player, otherPlayer->mo, minDist, draftdistance, leniency) == true)
|
||||
{
|
||||
return; // Finished doing our draft.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No one to draft off of? Then you can knock that off.
|
||||
if (player->draftleeway > 0) // Prevent small disruptions from stopping your draft.
|
||||
|
|
@ -2234,7 +2302,13 @@ static void K_UpdateDraft(player_t *player)
|
|||
{
|
||||
player_t *victim = &players[player->lastdraft];
|
||||
fixed_t dist = P_AproxDistance(P_AproxDistance(victim->mo->x - player->mo->x, victim->mo->y - player->mo->y), victim->mo->z - player->mo->z);
|
||||
K_DrawDraftCombiring(player, victim, dist, draftdistance, true);
|
||||
K_DrawDraftCombiring(player, victim->mo, dist, draftdistance, true);
|
||||
}
|
||||
else if (addUfo == true)
|
||||
{
|
||||
// kind of a hack to not have to mess with how lastdraft works
|
||||
fixed_t dist = P_AproxDistance(P_AproxDistance(specialStage.ufo->x - player->mo->x, specialStage.ufo->y - player->mo->y), specialStage.ufo->z - player->mo->z);
|
||||
K_DrawDraftCombiring(player, specialStage.ufo, dist, draftdistance, true);
|
||||
}
|
||||
}
|
||||
else // Remove draft speed boost.
|
||||
|
|
|
|||
|
|
@ -54,4 +54,8 @@ void Obj_DuelBombReverse(mobj_t *bomb);
|
|||
void Obj_DuelBombTouch(mobj_t *bomb, mobj_t *toucher);
|
||||
void Obj_DuelBombInit(mobj_t *bomb);
|
||||
|
||||
/* Special Stage UFO */
|
||||
void Obj_SpecialUFOThinker(mobj_t *bomb);
|
||||
mobj_t *Obj_CreateSpecialUFO(void);
|
||||
|
||||
#endif/*k_objects_H*/
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "st_stuff.h"
|
||||
#include "z_zone.h"
|
||||
#include "k_waypoint.h"
|
||||
#include "k_objects.h"
|
||||
|
||||
struct specialStage specialStage;
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ struct specialStage specialStage;
|
|||
--------------------------------------------------*/
|
||||
void K_ResetSpecialStage(void)
|
||||
{
|
||||
P_SetTarget(&specialStage.ufo, NULL);
|
||||
memset(&specialStage, 0, sizeof(struct specialStage));
|
||||
}
|
||||
|
||||
|
|
@ -43,6 +45,7 @@ void K_InitSpecialStage(void)
|
|||
INT32 i;
|
||||
|
||||
specialStage.beamDist = UINT32_MAX; // TODO: make proper value
|
||||
P_SetTarget(&specialStage.ufo, Obj_CreateSpecialUFO());
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ extern struct specialStage
|
|||
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars
|
||||
|
||||
UINT32 beamDist; ///< Where the exit beam is.
|
||||
mobj_t *capsule; ///< The Chaos Emerald capsule.
|
||||
mobj_t *ufo; ///< The Chaos Emerald capsule.
|
||||
} specialStage;
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1070,6 +1070,45 @@ static boolean K_WaypointPathfindReachedEnd(void *data, void *setupData)
|
|||
return isEnd;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_WaypointPathfindNextValid(void *data, void *setupData)
|
||||
|
||||
Returns if the current waypoint data has a next waypoint.
|
||||
|
||||
Input Arguments:-
|
||||
data - Should point to a pathfindnode_t to compare
|
||||
setupData - Should point to the pathfindsetup_t to compare
|
||||
|
||||
Return:-
|
||||
True if the waypoint has a next waypoint, false otherwise.
|
||||
--------------------------------------------------*/
|
||||
static boolean K_WaypointPathfindNextValid(void *data, void *setupData)
|
||||
{
|
||||
boolean nextValid = false;
|
||||
|
||||
if (data == NULL || setupData == NULL)
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindNextValid received NULL data.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
pathfindnode_t *node = (pathfindnode_t *)data;
|
||||
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
||||
waypoint_t *wp = (waypoint_t *)node->nodedata;
|
||||
|
||||
if (setup->getconnectednodes == K_WaypointPathfindGetPrev)
|
||||
{
|
||||
nextValid = (wp->numprevwaypoints > 0U);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextValid = (wp->numnextwaypoints > 0U);
|
||||
}
|
||||
}
|
||||
|
||||
return nextValid;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData)
|
||||
|
||||
|
|
@ -1094,8 +1133,9 @@ static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData)
|
|||
{
|
||||
pathfindnode_t *node = (pathfindnode_t *)data;
|
||||
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
||||
boolean nextValid = K_WaypointPathfindNextValid(data, setupData);
|
||||
|
||||
scoreReached = (node->gscore >= setup->endgscore);
|
||||
scoreReached = (node->gscore >= setup->endgscore) || (nextValid == false);
|
||||
}
|
||||
|
||||
return scoreReached;
|
||||
|
|
@ -1127,8 +1167,9 @@ static boolean K_WaypointPathfindReachedGScoreSpawnable(void *data, void *setupD
|
|||
pathfindnode_t *node = (pathfindnode_t *)data;
|
||||
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
||||
waypoint_t *wp = (waypoint_t *)node->nodedata;
|
||||
boolean nextValid = K_WaypointPathfindNextValid(data, setupData);
|
||||
|
||||
scoreReached = (node->gscore >= setup->endgscore);
|
||||
scoreReached = (node->gscore >= setup->endgscore) || (nextValid == false);
|
||||
spawnable = K_GetWaypointIsSpawnpoint(wp);
|
||||
}
|
||||
|
||||
|
|
@ -1251,13 +1292,6 @@ boolean K_PathfindThruCircuit(
|
|||
"K_PathfindThruCircuit: sourcewaypoint with ID %d has no next waypoint\n",
|
||||
K_GetWaypointID(sourcewaypoint));
|
||||
}
|
||||
else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0))
|
||||
|| ((huntbackwards == true) && (finishline->numnextwaypoints == 0)))
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC,
|
||||
"K_PathfindThruCircuit: finishline with ID %d has no previous waypoint\n",
|
||||
K_GetWaypointID(finishline));
|
||||
}
|
||||
else
|
||||
{
|
||||
pathfindsetup_t pathfindsetup = {0};
|
||||
|
|
@ -1334,13 +1368,6 @@ boolean K_PathfindThruCircuitSpawnable(
|
|||
"K_PathfindThruCircuitSpawnable: sourcewaypoint with ID %d has no next waypoint\n",
|
||||
K_GetWaypointID(sourcewaypoint));
|
||||
}
|
||||
else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0))
|
||||
|| ((huntbackwards == true) && (finishline->numnextwaypoints == 0)))
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC,
|
||||
"K_PathfindThruCircuitSpawnable: finishline with ID %d has no previous waypoint\n",
|
||||
K_GetWaypointID(finishline));
|
||||
}
|
||||
else
|
||||
{
|
||||
pathfindsetup_t pathfindsetup = {0};
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@ manta-ring.c
|
|||
orbinaut.c
|
||||
jawz.c
|
||||
duel-bomb.c
|
||||
ufo.c
|
||||
|
|
|
|||
375
src/objects/ufo.c
Normal file
375
src/objects/ufo.c
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
|
||||
// Copyright (C) 2022 by Kart Krew
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file shrink.c
|
||||
/// \brief Shrink laser item code.
|
||||
|
||||
#include "../doomdef.h"
|
||||
#include "../doomstat.h"
|
||||
#include "../info.h"
|
||||
#include "../k_kart.h"
|
||||
#include "../k_objects.h"
|
||||
#include "../m_random.h"
|
||||
#include "../p_local.h"
|
||||
#include "../r_main.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../g_game.h"
|
||||
#include "../z_zone.h"
|
||||
#include "../k_waypoint.h"
|
||||
|
||||
#define UFO_BASE_SPEED (12 * FRACUNIT) // UFO's slowest speed.
|
||||
#define UFO_SPEEDUP (FRACUNIT)
|
||||
#define UFO_SLOWDOWN (FRACUNIT >> 2)
|
||||
#define UFO_SPACING (1024 * FRACUNIT)
|
||||
#define UFO_DEADZONE (512 * FRACUNIT)
|
||||
#define UFO_SPEEDFACTOR (FRACUNIT * 9 / 10)
|
||||
|
||||
#define ufo_waypoint(o) ((o)->extravalue1)
|
||||
#define ufo_distancetofinish(o) ((o)->extravalue2)
|
||||
#define ufo_speed(o) ((o)->watertop)
|
||||
|
||||
static void UFOMoveTo(mobj_t *ufo, fixed_t destx, fixed_t desty, fixed_t destz)
|
||||
{
|
||||
ufo->momx = destx - ufo->x;
|
||||
ufo->momy = desty - ufo->y;
|
||||
ufo->momz = destz - ufo->z;
|
||||
}
|
||||
|
||||
static fixed_t GenericDistance(
|
||||
fixed_t curx, fixed_t cury, fixed_t curz,
|
||||
fixed_t destx, fixed_t desty, fixed_t destz)
|
||||
{
|
||||
return P_AproxDistance(P_AproxDistance(destx - curx, desty - cury), destz - curz);
|
||||
}
|
||||
|
||||
static void UFOUpdateDistanceToFinish(mobj_t *ufo)
|
||||
{
|
||||
waypoint_t *finishLine = K_GetFinishLineWaypoint();
|
||||
waypoint_t *nextWaypoint = K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo));
|
||||
|
||||
if (nextWaypoint != NULL && finishLine != NULL)
|
||||
{
|
||||
const boolean useshortcuts = false;
|
||||
const boolean huntbackwards = false;
|
||||
boolean pathfindsuccess = false;
|
||||
path_t pathtofinish = {0};
|
||||
|
||||
pathfindsuccess =
|
||||
K_PathfindToWaypoint(nextWaypoint, finishLine, &pathtofinish, useshortcuts, huntbackwards);
|
||||
|
||||
// Update the UFO's distance to the finish line if a path was found.
|
||||
if (pathfindsuccess == true)
|
||||
{
|
||||
// Add euclidean distance to the next waypoint to the distancetofinish
|
||||
UINT32 adddist;
|
||||
fixed_t disttowaypoint =
|
||||
P_AproxDistance(
|
||||
(ufo->x >> FRACBITS) - (nextWaypoint->mobj->x >> FRACBITS),
|
||||
(ufo->y >> FRACBITS) - (nextWaypoint->mobj->y >> FRACBITS));
|
||||
disttowaypoint = P_AproxDistance(disttowaypoint, (ufo->z >> FRACBITS) - (nextWaypoint->mobj->z >> FRACBITS));
|
||||
|
||||
adddist = (UINT32)disttowaypoint;
|
||||
|
||||
ufo_distancetofinish(ufo) = pathtofinish.totaldist + adddist;
|
||||
Z_Free(pathtofinish.array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UFOUpdateSpeed(mobj_t *ufo)
|
||||
{
|
||||
const fixed_t baseSpeed = FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed));
|
||||
const UINT32 spacing = FixedMul(FixedMul(UFO_SPACING, mapobjectscale), K_GetKartGameSpeedScalar(gamespeed)) >> FRACBITS;
|
||||
const UINT32 deadzone = FixedMul(FixedMul(UFO_DEADZONE, mapobjectscale), K_GetKartGameSpeedScalar(gamespeed)) >> FRACBITS;
|
||||
|
||||
// Best values of all of the players.
|
||||
UINT32 bestDist = UINT32_MAX;
|
||||
fixed_t bestSpeed = 0;
|
||||
|
||||
// Desired values for the UFO itself.
|
||||
UINT32 wantedDist = UINT32_MAX;
|
||||
fixed_t wantedSpeed = ufo_speed(ufo);
|
||||
fixed_t speedDelta = 0;
|
||||
|
||||
UINT8 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *player = NULL;
|
||||
|
||||
if (playeringame[i] == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
player = &players[i];
|
||||
if (player->spectator == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->distancetofinish < bestDist)
|
||||
{
|
||||
bestDist = player->distancetofinish;
|
||||
|
||||
// Doesn't matter if a splitscreen player behind is moving faster behind the one most caught up.
|
||||
bestSpeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy);
|
||||
|
||||
bestSpeed = FixedDiv(bestSpeed, mapobjectscale); // Unscale from mapobjectscale to FRACUNIT
|
||||
bestSpeed = FixedMul(bestSpeed, UFO_SPEEDFACTOR); // Make it a bit more lenient
|
||||
}
|
||||
}
|
||||
|
||||
if (bestDist == UINT32_MAX)
|
||||
{
|
||||
// Invalid, lets go back to base speed.
|
||||
wantedSpeed = baseSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
INT32 distDelta = 0;
|
||||
|
||||
if (bestDist > spacing)
|
||||
{
|
||||
wantedDist = bestDist - spacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
wantedDist = 0;
|
||||
}
|
||||
|
||||
distDelta = ufo_distancetofinish(ufo) - wantedDist;
|
||||
|
||||
if (abs(distDelta) <= deadzone)
|
||||
{
|
||||
// We're in a good spot, try to match the player.
|
||||
wantedSpeed = max(bestSpeed >> 1, baseSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (distDelta > 0)
|
||||
{
|
||||
// Too far behind! Start speeding up!
|
||||
wantedSpeed = max(bestSpeed << 1, baseSpeed << 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too far ahead! Start slowing down!
|
||||
wantedSpeed = baseSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slowly accelerate or decelerate to
|
||||
// get to our desired speed.
|
||||
speedDelta = wantedSpeed - ufo_speed(ufo);
|
||||
if (speedDelta > 0)
|
||||
{
|
||||
if (abs(speedDelta) <= UFO_SPEEDUP)
|
||||
{
|
||||
ufo_speed(ufo) = wantedSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
ufo_speed(ufo) += UFO_SPEEDUP;
|
||||
}
|
||||
}
|
||||
else if (speedDelta < 0)
|
||||
{
|
||||
if (abs(speedDelta) <= UFO_SLOWDOWN)
|
||||
{
|
||||
ufo_speed(ufo) = wantedSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
ufo_speed(ufo) -= UFO_SLOWDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UFOUpdateAngle(mobj_t *ufo)
|
||||
{
|
||||
angle_t dest = K_MomentumAngle(ufo);
|
||||
INT32 delta = AngleDeltaSigned(ufo->angle, dest);
|
||||
ufo->angle += delta >> 2;
|
||||
}
|
||||
|
||||
static void UFOMove(mobj_t *ufo)
|
||||
{
|
||||
waypoint_t *curWaypoint = NULL;
|
||||
waypoint_t *destWaypoint = NULL;
|
||||
|
||||
fixed_t distLeft = INT32_MAX;
|
||||
fixed_t newX = ufo->x;
|
||||
fixed_t newY = ufo->y;
|
||||
fixed_t newZ = ufo->z;
|
||||
|
||||
const boolean useshortcuts = false;
|
||||
const boolean huntbackwards = false;
|
||||
boolean pathfindsuccess = false;
|
||||
path_t pathtofinish = {0};
|
||||
size_t pathIndex = 0;
|
||||
|
||||
curWaypoint = K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo));
|
||||
destWaypoint = K_GetFinishLineWaypoint();
|
||||
|
||||
if (curWaypoint == NULL || destWaypoint == NULL)
|
||||
{
|
||||
// Waypoints aren't valid.
|
||||
// Just stand still.
|
||||
ufo->momx = 0;
|
||||
ufo->momy = 0;
|
||||
ufo->momz = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
distLeft = FixedMul(ufo_speed(ufo), mapobjectscale);
|
||||
|
||||
while (distLeft > 0)
|
||||
{
|
||||
fixed_t wpX = curWaypoint->mobj->x;
|
||||
fixed_t wpY = curWaypoint->mobj->y;
|
||||
fixed_t wpZ = curWaypoint->mobj->z;
|
||||
|
||||
fixed_t distToNext = GenericDistance(
|
||||
newX, newY, newZ,
|
||||
wpX, wpY, wpZ
|
||||
);
|
||||
|
||||
if (distToNext > distLeft)
|
||||
{
|
||||
// Only made it partially there.
|
||||
newX += FixedMul(FixedDiv(wpX - newX, distToNext), distLeft);
|
||||
newY += FixedMul(FixedDiv(wpY - newY, distToNext), distLeft);
|
||||
newZ += FixedMul(FixedDiv(wpZ - newZ, distToNext), distLeft);
|
||||
|
||||
distLeft = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Close enough to the next waypoint,
|
||||
// move there and remove the distance.
|
||||
newX = wpX;
|
||||
newY = wpY;
|
||||
newZ = wpZ;
|
||||
|
||||
distLeft -= distToNext;
|
||||
|
||||
if (curWaypoint == destWaypoint)
|
||||
{
|
||||
// Reached the end.
|
||||
break;
|
||||
}
|
||||
|
||||
// Create waypoint path to our destination.
|
||||
// Crazy over-engineered, just to catch when
|
||||
// waypoints are insanely close to each other :P
|
||||
if (pathfindsuccess == false)
|
||||
{
|
||||
pathfindsuccess = K_PathfindToWaypoint(
|
||||
curWaypoint, destWaypoint,
|
||||
&pathtofinish,
|
||||
useshortcuts, huntbackwards
|
||||
);
|
||||
|
||||
if (pathfindsuccess == false)
|
||||
{
|
||||
// Path isn't valid.
|
||||
// Just transition into the next state.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pathIndex++;
|
||||
|
||||
if (pathIndex >= pathtofinish.numnodes)
|
||||
{
|
||||
// Successfully reached the end of the path.
|
||||
break;
|
||||
}
|
||||
|
||||
// Now moving to the next waypoint.
|
||||
curWaypoint = (waypoint_t *)pathtofinish.array[pathIndex].nodedata;
|
||||
ufo_waypoint(ufo) = (INT32)K_GetWaypointHeapIndex(curWaypoint);
|
||||
}
|
||||
}
|
||||
|
||||
UFOMoveTo(ufo, newX, newY, newZ);
|
||||
|
||||
if (pathfindsuccess == true)
|
||||
{
|
||||
Z_Free(pathtofinish.array);
|
||||
}
|
||||
}
|
||||
|
||||
void Obj_SpecialUFOThinker(mobj_t *ufo)
|
||||
{
|
||||
UFOMove(ufo);
|
||||
UFOUpdateAngle(ufo);
|
||||
UFOUpdateDistanceToFinish(ufo);
|
||||
UFOUpdateSpeed(ufo);
|
||||
}
|
||||
|
||||
static mobj_t *InitSpecialUFO(waypoint_t *start)
|
||||
{
|
||||
mobj_t *ufo = NULL;
|
||||
|
||||
if (start == NULL)
|
||||
{
|
||||
// Simply create at the origin with default values.
|
||||
ufo = P_SpawnMobj(0, 0, 0, MT_SPECIAL_UFO);
|
||||
ufo_waypoint(ufo) = -1; // Invalidate
|
||||
ufo_distancetofinish(ufo) = INT32_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create with a proper waypoint track!
|
||||
ufo = P_SpawnMobj(start->mobj->x, start->mobj->y, start->mobj->z, MT_SPECIAL_UFO);
|
||||
ufo_waypoint(ufo) = (INT32)K_GetWaypointHeapIndex(start);
|
||||
UFOUpdateDistanceToFinish(ufo);
|
||||
}
|
||||
|
||||
ufo_speed(ufo) = UFO_BASE_SPEED;
|
||||
|
||||
return ufo;
|
||||
}
|
||||
|
||||
mobj_t *Obj_CreateSpecialUFO(void)
|
||||
{
|
||||
waypoint_t *finishWaypoint = K_GetFinishLineWaypoint();
|
||||
waypoint_t *startWaypoint = NULL;
|
||||
|
||||
if (finishWaypoint != NULL)
|
||||
{
|
||||
const boolean huntbackwards = true;
|
||||
const boolean useshortcuts = false;
|
||||
const UINT32 traveldist = UINT32_MAX; // Go as far back as possible.
|
||||
boolean pathfindsuccess = false;
|
||||
path_t pathtofinish = {0};
|
||||
|
||||
pathfindsuccess = K_PathfindThruCircuit(
|
||||
finishWaypoint, traveldist,
|
||||
&pathtofinish,
|
||||
useshortcuts, huntbackwards
|
||||
);
|
||||
|
||||
if (pathfindsuccess == true)
|
||||
{
|
||||
startWaypoint = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata;
|
||||
Z_Free(pathtofinish.array);
|
||||
}
|
||||
}
|
||||
|
||||
return InitSpecialUFO(startWaypoint);
|
||||
}
|
||||
|
|
@ -7176,6 +7176,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
Obj_DuelBombThink(mobj);
|
||||
break;
|
||||
}
|
||||
case MT_SPECIAL_UFO:
|
||||
{
|
||||
Obj_SpecialUFOThinker(mobj);
|
||||
break;
|
||||
}
|
||||
case MT_EMERALD:
|
||||
{
|
||||
if (battleovertime.enabled >= 10*TICRATE)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue