From 8ca3d41696d3c3428bf5209be840585c0087f884 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 21 Nov 2022 23:51:26 -0500 Subject: [PATCH 01/40] 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. --- src/deh_tables.c | 2 + src/info.c | 27 +++ src/info.h | 2 + src/k_kart.c | 260 ++++++++++++++++++---------- src/k_objects.h | 4 + src/k_specialstage.c | 3 + src/k_specialstage.h | 2 +- src/k_waypoint.c | 59 +++++-- src/objects/Sourcefile | 1 + src/objects/ufo.c | 375 +++++++++++++++++++++++++++++++++++++++++ src/p_mobj.c | 5 + 11 files changed, 630 insertions(+), 110 deletions(-) create mode 100644 src/objects/ufo.c diff --git a/src/deh_tables.c b/src/deh_tables.c index aa9dc9310..2198466f5 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -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[] = { diff --git a/src/info.c b/src/info.c index 44a4d25ae..47d593aac 100644 --- a/src/info.c +++ b/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] = { diff --git a/src/info.h b/src/info.h index 78fc6e2db..02f69263a 100644 --- a/src/info.h +++ b/src/info.h @@ -6667,6 +6667,8 @@ typedef enum mobj_type MT_BEAMPOINT, + MT_SPECIAL_UFO, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_kart.c b/src/k_kart.c index 40d1ff0a2..f781c64cb 100644 --- a/src/k_kart.c +++ b/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; } - 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; @@ -2116,104 +2245,43 @@ static void K_UpdateDraft(player_t *player) } // Not enough speed to draft. - if (player->speed >= 20*player->mo->scale) + 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; - } - - if (gametype == GT_BATTLE) - { - // TODO: gametyperules - // Double speed in Battle - player->draftpower += add; - } + continue; } - if (player->draftpower > FRACUNIT) - player->draftpower = FRACUNIT; + otherPlayer = &players[i]; - // Play draft finish noise - if (olddraft < FRACUNIT && player->draftpower >= FRACUNIT) - S_StartSound(player->mo, sfx_cdfm62); + if (otherPlayer->spectator == true) + { + continue; + } - // Spawn in the visual! - K_DrawDraftCombiring(player, &players[i], dist, draftdistance, false); + if (otherPlayer->mo == NULL || P_MobjWasRemoved(otherPlayer->mo) == true) + { + continue; + } - return; // Finished doing our draft. + if (K_TryDraft(player, otherPlayer->mo, minDist, draftdistance, leniency) == true) + { + return; // Finished doing our 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. diff --git a/src/k_objects.h b/src/k_objects.h index 96e0fa2b5..127435c91 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -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*/ diff --git a/src/k_specialstage.c b/src/k_specialstage.c index 3a2d751ac..3700d98fb 100644 --- a/src/k_specialstage.c +++ b/src/k_specialstage.c @@ -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++) { diff --git a/src/k_specialstage.h b/src/k_specialstage.h index 8e11d761d..c93136b99 100644 --- a/src/k_specialstage.h +++ b/src/k_specialstage.h @@ -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; /*-------------------------------------------------- diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 28ff00d04..4577b4f55 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -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}; diff --git a/src/objects/Sourcefile b/src/objects/Sourcefile index b8cb63b1f..1db7b4a33 100644 --- a/src/objects/Sourcefile +++ b/src/objects/Sourcefile @@ -7,3 +7,4 @@ manta-ring.c orbinaut.c jawz.c duel-bomb.c +ufo.c diff --git a/src/objects/ufo.c b/src/objects/ufo.c new file mode 100644 index 000000000..8b5dfaf6a --- /dev/null +++ b/src/objects/ufo.c @@ -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); +} diff --git a/src/p_mobj.c b/src/p_mobj.c index d5924de51..d2ec29404 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -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) From b55d299fdbb7b8bf0a7b962af34c22f31fb0c9f2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 00:39:53 -0500 Subject: [PATCH 02/40] Implement the Special Stage item table --- src/k_hud.c | 20 +---- src/k_kart.c | 196 +++++++++++++++++++++++++++++++--------------- src/k_kart.h | 1 + src/k_objects.h | 1 + src/objects/ufo.c | 14 ++++ 5 files changed, 149 insertions(+), 83 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 4a5644442..6a79a94e1 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4570,25 +4570,7 @@ static void K_drawDistributionDebugger(void) bestbumper = players[i].bumpers; } - // lovely double loop...... - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator - && players[i].position == 1) - { - // This player is first! Yay! - pdis = stplyr->distancetofinish - players[i].distancetofinish; - break; - } - } - - pdis = K_ScaleItemDistance(pdis, pingame); - - if (stplyr->bot && stplyr->botvars.rival) - { - // Rival has better odds :) - pdis = (15 * pdis) / 14; - } + pdis = K_GetItemRouletteDistance(stplyr, pingame); useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper); diff --git a/src/k_kart.c b/src/k_kart.c index f781c64cb..2505d7ea8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -485,8 +485,6 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] = { 5, 1 } // Jawz x2 }; -// TODO: add back when this gets used -#if 0 static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] = { //M N O P @@ -519,7 +517,6 @@ static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] = { 0, 0, 1, 1 }, // Orbinaut x4 { 0, 0, 1, 1 } // Jawz x2 }; -#endif #define DISTVAR (2048) // Magic number distance for use with item roulette tiers #define SPBSTARTDIST (6*DISTVAR) // Distance when SPB can start appearing @@ -797,6 +794,11 @@ INT32 K_KartGetItemOdds( I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table newodds = K_KartItemOddsBattle[item-1][pos]; } + else if (specialStage.active == true) + { + I_Assert(pos < 4); // Ditto + newodds = K_KartItemOddsSpecial[item-1][pos]; + } else { I_Assert(pos < 8); // Ditto @@ -902,29 +904,32 @@ INT32 K_KartGetItemOdds( cooldownOnStart = true; notNearEnd = true; - if (firstDist < ENDDIST*2 // No SPB when 1st is almost done - || isFirst == true) // No SPB for 1st ever + if (specialStage.active == false) { - newodds = 0; - } - else - { - const UINT32 dist = max(0, ((signed)secondToFirst) - SPBSTARTDIST); - const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST; - const UINT8 maxOdds = 20; - fixed_t multiplier = (dist * FRACUNIT) / distRange; - - if (multiplier < 0) + if (firstDist < ENDDIST*2 // No SPB when 1st is almost done + || isFirst == true) // No SPB for 1st ever { - multiplier = 0; + newodds = 0; } - - if (multiplier > FRACUNIT) + else { - multiplier = FRACUNIT; - } + const UINT32 dist = max(0, ((signed)secondToFirst) - SPBSTARTDIST); + const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST; + const UINT8 maxOdds = 20; + fixed_t multiplier = (dist * FRACUNIT) / distRange; - newodds = FixedMul(maxOdds * 4, multiplier); + if (multiplier < 0) + { + multiplier = 0; + } + + if (multiplier > FRACUNIT) + { + multiplier = FRACUNIT; + } + + newodds = FixedMul(maxOdds * 4, multiplier); + } } break; @@ -1005,6 +1010,7 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum UINT8 useodds = 0; UINT8 disttable[14]; UINT8 distlen = 0; + UINT8 totalsize = 0; boolean oddsvalid[8]; // Unused now, oops :V @@ -1015,10 +1021,15 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum UINT8 j; boolean available = false; - if (gametype == GT_BATTLE && i > 1) + if (specialStage.active == true && i > 3) { oddsvalid[i] = false; - break; + continue; + } + else if (gametype == GT_BATTLE && i > 1) + { + oddsvalid[i] = false; + continue; } for (j = 1; j < NUMKARTRESULTS; j++) @@ -1039,6 +1050,7 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum } #define SETUPDISTTABLE(odds, num) \ + totalsize++;\ if (oddsvalid[odds]) \ for (i = num; i; --i) \ disttable[distlen++] = odds; @@ -1063,26 +1075,44 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum } else { - SETUPDISTTABLE(0,1); - SETUPDISTTABLE(1,1); - SETUPDISTTABLE(2,1); - SETUPDISTTABLE(3,2); - SETUPDISTTABLE(4,2); - SETUPDISTTABLE(5,3); - SETUPDISTTABLE(6,3); - SETUPDISTTABLE(7,1); + UINT32 itotaldis = 0; + + if (specialStage.active == true) // Special Stages + { + SETUPDISTTABLE(0,1); + SETUPDISTTABLE(1,2); + SETUPDISTTABLE(2,3); + SETUPDISTTABLE(3,1); + } + else // Race + { + SETUPDISTTABLE(0,1); + SETUPDISTTABLE(1,1); + SETUPDISTTABLE(2,1); + SETUPDISTTABLE(3,2); + SETUPDISTTABLE(4,2); + SETUPDISTTABLE(5,3); + SETUPDISTTABLE(6,3); + SETUPDISTTABLE(7,1); + } + + itotaldis = DISTVAR * (((totalsize - 2) * distlen) / totalsize); if (pdis == 0) + { useodds = disttable[0]; - else if (pdis > DISTVAR * ((12 * distlen) / 14)) + } + else if (pdis > itotaldis) + { useodds = disttable[distlen-1]; + } else { - for (i = 1; i < 13; i++) + for (i = 1; i < totalsize-1; i++) { - if (pdis <= DISTVAR * ((i * distlen) / 14)) + if (pdis <= DISTVAR * ((i * distlen) / totalsize)) { - useodds = disttable[((i * distlen) / 14)]; + useodds = disttable[((i * distlen) / totalsize)]; break; } } @@ -1118,6 +1148,11 @@ INT32 K_GetRollingRouletteItem(player_t *player) odds_row = K_KartItemOddsBattle[0]; odds_row_size = sizeof K_KartItemOddsBattle[0]; } + else if (specialStage.active == true) + { + odds_row = K_KartItemOddsSpecial[0]; + odds_row_size = sizeof K_KartItemOddsSpecial[0]; + } else { odds_row = K_KartItemOddsRace[0]; @@ -1160,6 +1195,11 @@ boolean K_ForcedSPB(player_t *player) return false; } + if (specialStage.active == true) + { + return false; + } + if (player->position <= 1) { return false; @@ -1216,6 +1256,61 @@ boolean K_ForcedSPB(player_t *player) return (secondToFirst >= SPBFORCEDIST); } +UINT32 K_GetItemRouletteDistance(player_t *player, UINT8 pingame) +{ + UINT32 pdis = 0; + + if (specialStage.active == true) + { + UINT32 ufoDis = K_GetSpecialUFODistance(); + + if (player->distancetofinish <= ufoDis) + { + // You're ahead of the UFO. + pdis = 0; + } + else + { + // Subtract the UFO's distance from your distance! + pdis = player->distancetofinish - ufoDis; + } + } + else + { + UINT8 i; + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator + && players[i].position == 1) + { + // This player is first! Yay! + + if (player->distancetofinish <= players[i].distancetofinish) + { + // Guess you're in first / tied for first? + pdis = 0; + } + else + { + // Subtract 1st's distance from your distance, to get your distance from 1st! + pdis = player->distancetofinish - players[i].distancetofinish; + } + break; + } + } + } + + pdis = K_ScaleItemDistance(pdis, pingame); + + if (player->bot && player->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + + return pdis; +} + static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) { INT32 i; @@ -1271,34 +1366,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) else if (!(player->itemroulette >= (TICRATE*3))) return; - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator - && players[i].position == 1) - { - // This player is first! Yay! - - if (player->distancetofinish <= players[i].distancetofinish) - { - // Guess you're in first / tied for first? - pdis = 0; - } - else - { - // Subtract 1st's distance from your distance, to get your distance from 1st! - pdis = player->distancetofinish - players[i].distancetofinish; - } - break; - } - } - - pdis = K_ScaleItemDistance(pdis, pingame); - - if (player->bot && player->botvars.rival) - { - // Rival has better odds :) - pdis = (15 * pdis) / 14; - } + pdis = K_GetItemRouletteDistance(player, pingame); // SPECIAL CASE No. 1: // Fake Eggman items @@ -1331,7 +1399,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) // SPECIAL CASE No. 3: // Record Attack / alone mashing behavior - if (modeattacking || pingame == 1) + if ((modeattacking || pingame == 1) && (specialStage.active == false)) { if (gametype == GT_RACE) { diff --git a/src/k_kart.h b/src/k_kart.h index a5d2b0185..b12da338e 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -55,6 +55,7 @@ UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers); INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, fixed_t mashed, boolean bot, boolean rival); INT32 K_GetRollingRouletteItem(player_t *player); boolean K_ForcedSPB(player_t *player); +UINT32 K_GetItemRouletteDistance(player_t *player, UINT8 pingame); INT32 K_GetShieldFromItem(INT32 item); SINT8 K_ItemResultToType(SINT8 getitem); UINT8 K_ItemResultToAmount(SINT8 getitem); diff --git a/src/k_objects.h b/src/k_objects.h index 127435c91..7564fcc20 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -57,5 +57,6 @@ void Obj_DuelBombInit(mobj_t *bomb); /* Special Stage UFO */ void Obj_SpecialUFOThinker(mobj_t *bomb); mobj_t *Obj_CreateSpecialUFO(void); +UINT32 K_GetSpecialUFODistance(void); #endif/*k_objects_H*/ diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 8b5dfaf6a..3d2ad68d7 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -22,6 +22,7 @@ #include "../g_game.h" #include "../z_zone.h" #include "../k_waypoint.h" +#include "../k_specialstage.h" #define UFO_BASE_SPEED (12 * FRACUNIT) // UFO's slowest speed. #define UFO_SPEEDUP (FRACUNIT) @@ -373,3 +374,16 @@ mobj_t *Obj_CreateSpecialUFO(void) return InitSpecialUFO(startWaypoint); } + +UINT32 K_GetSpecialUFODistance(void) +{ + if (specialStage.active == true) + { + if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false) + { + return (UINT32)ufo_distancetofinish(specialStage.ufo); + } + } + + return UINT32_MAX; +} From 9a26a91264c1355cdda647781d75eeb262647200 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 16:31:30 -0500 Subject: [PATCH 03/40] Lots more UFO work - UFO is able to be damaged. (No damage values set yet, so it has an obnoxious amount of health.) - Emerald becomes collectible when fully damaged. - Jawz can target the UFO. - Tweaked some of the speed values. --- src/info.c | 2 +- src/k_kart.c | 63 +++++++++++++++++++++----------- src/k_kart.h | 2 +- src/k_objects.h | 1 + src/objects/jawz.c | 18 +++++---- src/objects/ufo.c | 91 +++++++++++++++++++++++++++++++++++++++++++--- src/p_inter.c | 5 +++ 7 files changed, 146 insertions(+), 36 deletions(-) diff --git a/src/info.c b/src/info.c index 47d593aac..3faf15dd0 100644 --- a/src/info.c +++ b/src/info.c @@ -28994,7 +28994,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_SPECIAL_UFO -1, // doomednum S_CHAOSEMERALD1, // spawnstate - 1000, // spawnhealth + 101, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime diff --git a/src/k_kart.c b/src/k_kart.c index 2505d7ea8..ca030f88a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7666,12 +7666,18 @@ static void K_MoveHeldObjects(player_t *player) } } -player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) +mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) { fixed_t best = INT32_MAX; - player_t *wtarg = NULL; + mobj_t *wtarg = NULL; INT32 i; + if (specialStage.active == true) + { + // Always target the UFO. + return specialStage.ufo; + } + for (i = 0; i < MAXPLAYERS; i++) { angle_t thisang = ANGLE_MAX; @@ -7687,7 +7693,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) player = &players[i]; // Don't target yourself, stupid. - if (player == source) + if (source != NULL && player == source) { continue; } @@ -7726,7 +7732,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) if (gametyperules & GTR_CIRCUIT) { - if (player->position >= source->position) + if (source != NULL && player->position >= source->position) { // Don't pay attention to people who aren't above your position continue; @@ -7768,7 +7774,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) if (thisScore < best) { - wtarg = player; + wtarg = player->mo; best = thisScore; } } @@ -8877,24 +8883,32 @@ void K_KartPlayerAfterThink(player_t *player) // Jawz reticule (seeking) if (player->itemtype == KITEM_JAWZ && (player->pflags & PF_ITEMOUT)) { - INT32 lastTargID = player->lastjawztarget; - player_t *lastTarg = NULL; - player_t *targ = NULL; + const INT32 lastTargID = player->lastjawztarget; + mobj_t *lastTarg = NULL; + + INT32 targID = MAXPLAYERS; + mobj_t *targ = NULL; + mobj_t *ret = NULL; - if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS) + if (specialStage.active == true && lastTargID == MAXPLAYERS) + { + // Aiming at the UFO. + lastTarg = specialStage.ufo; + } + else if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS) && playeringame[lastTargID] == true) { if (players[lastTargID].spectator == false) { - lastTarg = &players[lastTargID]; + lastTarg = players[lastTargID].mo; } } if (player->throwdir == -1) { // Backwards Jawz targets yourself. - targ = player; + targ = player->mo; player->jawztargetdelay = 0; } else @@ -8903,9 +8917,14 @@ void K_KartPlayerAfterThink(player_t *player) targ = K_FindJawzTarget(player->mo, player, ANGLE_45); } - if (targ != NULL && targ->mo != NULL && P_MobjWasRemoved(targ->mo) == false) + if (targ != NULL && P_MobjWasRemoved(targ) == false) { - if (targ - players == lastTargID) + if (targ->player != NULL) + { + targID = targ->player - players; + } + + if (targID == lastTargID) { // Increment delay. if (player->jawztargetdelay < 10) @@ -8924,33 +8943,33 @@ void K_KartPlayerAfterThink(player_t *player) else { // Allow a swap. - if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ)) + if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ->player)) { S_StartSound(NULL, sfx_s3k89); } else { - S_StartSound(targ->mo, sfx_s3k89); + S_StartSound(targ, sfx_s3k89); } - player->lastjawztarget = targ - players; + player->lastjawztarget = targID; player->jawztargetdelay = 5; } } } - if (targ == NULL || targ->mo == NULL || P_MobjWasRemoved(targ->mo) == true) + if (targ == NULL || P_MobjWasRemoved(targ) == true) { player->lastjawztarget = -1; player->jawztargetdelay = 0; return; } - ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE); - ret->old_x = targ->mo->old_x; - ret->old_y = targ->mo->old_y; - ret->old_z = targ->mo->old_z; - P_SetTarget(&ret->target, targ->mo); + ret = P_SpawnMobj(targ->x, targ->y, targ->z, MT_PLAYERRETICULE); + ret->old_x = targ->old_x; + ret->old_y = targ->old_y; + ret->old_z = targ->old_z; + P_SetTarget(&ret->target, targ); ret->frame |= ((leveltime % 10) / 2); ret->tics = 1; ret->color = player->skincolor; diff --git a/src/k_kart.h b/src/k_kart.h index b12da338e..8f43a6f37 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -123,7 +123,7 @@ void K_UpdateHnextList(player_t *player, boolean clean); void K_DropHnextList(player_t *player, boolean keepshields); void K_RepairOrbitChain(mobj_t *orbit); void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player); -player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range); +mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range); INT32 K_GetKartRingPower(player_t *player, boolean boosted); void K_UpdateDistanceFromFinishLine(player_t *const player); boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y); diff --git a/src/k_objects.h b/src/k_objects.h index 7564fcc20..a8daaf50b 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -56,6 +56,7 @@ void Obj_DuelBombInit(mobj_t *bomb); /* Special Stage UFO */ void Obj_SpecialUFOThinker(mobj_t *bomb); +boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType); mobj_t *Obj_CreateSpecialUFO(void); UINT32 K_GetSpecialUFODistance(void); diff --git a/src/objects/jawz.c b/src/objects/jawz.c index cc241ba87..52a52a95c 100644 --- a/src/objects/jawz.c +++ b/src/objects/jawz.c @@ -140,17 +140,21 @@ static void JawzChase(mobj_t *th, boolean grounded) if (jawz_chase(th) == NULL || P_MobjWasRemoved(jawz_chase(th)) == true) { + mobj_t *newChase = NULL; + player_t *owner = NULL; + th->angle = K_MomentumAngle(th); - if (jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false - && jawz_owner(th)->player != NULL) + if ((jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false) + && (jawz_owner(th)->player != NULL)) { - player_t *newPlayer = K_FindJawzTarget(th, jawz_owner(th)->player, ANGLE_90); + owner = jawz_owner(th)->player; + } - if (newPlayer != NULL) - { - P_SetTarget(&jawz_chase(th), newPlayer->mo); - } + newChase = K_FindJawzTarget(th, owner, ANGLE_90); + if (newChase != NULL) + { + P_SetTarget(&jawz_chase(th), newChase); } } diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 3d2ad68d7..fa26ef51d 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -7,8 +7,8 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file shrink.c -/// \brief Shrink laser item code. +/// \file ufo.c +/// \brief Special Stage UFO #include "../doomdef.h" #include "../doomstat.h" @@ -24,11 +24,11 @@ #include "../k_waypoint.h" #include "../k_specialstage.h" -#define UFO_BASE_SPEED (12 * FRACUNIT) // UFO's slowest speed. -#define UFO_SPEEDUP (FRACUNIT) +#define UFO_BASE_SPEED (16 * FRACUNIT) // UFO's slowest speed. +#define UFO_SPEEDUP (FRACUNIT >> 3) #define UFO_SLOWDOWN (FRACUNIT >> 2) #define UFO_SPACING (1024 * FRACUNIT) -#define UFO_DEADZONE (512 * FRACUNIT) +#define UFO_DEADZONE (768 * FRACUNIT) #define UFO_SPEEDFACTOR (FRACUNIT * 9 / 10) #define ufo_waypoint(o) ((o)->extravalue1) @@ -49,6 +49,11 @@ static fixed_t GenericDistance( return P_AproxDistance(P_AproxDistance(destx - curx, desty - cury), destz - curz); } +static boolean UFOEmeraldChase(mobj_t *ufo) +{ + return (ufo->health <= 1); +} + static void UFOUpdateDistanceToFinish(mobj_t *ufo) { waypoint_t *finishLine = K_GetFinishLineWaypoint(); @@ -314,17 +319,86 @@ static void UFOMove(mobj_t *ufo) } } +static void UFOEmeraldVFX(mobj_t *ufo) +{ + if (leveltime % 3 == 0) + { + mobj_t *sparkle = P_SpawnMobjFromMobj( + ufo, + P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT, + P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT, + P_RandomRange(PR_SPARKLE, 0, 64) * FRACUNIT, + MT_EMERALDSPARK + ); + + sparkle->color = ufo->color; + sparkle->momz += 8 * ufo->scale * P_MobjFlip(ufo); + } +} + void Obj_SpecialUFOThinker(mobj_t *ufo) { UFOMove(ufo); UFOUpdateAngle(ufo); UFOUpdateDistanceToFinish(ufo); UFOUpdateSpeed(ufo); + + if (UFOEmeraldChase(ufo) == true) + { + // Spawn emerald sparkles + UFOEmeraldVFX(ufo); + } +} + +static UINT8 GetUFODamage(mobj_t *inflictor) +{ + if (inflictor == NULL || P_MobjWasRemoved(inflictor) == true) + { + return 1; + } + + switch (inflictor->type) + { + default: + { + return 1; + } + } +} + +boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType) +{ + UINT8 damage = 1; + + (void)source; + (void)damageType; + + if (UFOEmeraldChase(ufo) == true) + { + // Damaged fully already, no need for any more. + ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); // Double check flags, just to be sure. + return false; + } + + damage = GetUFODamage(inflictor); + + if (damage >= ufo->health - 1) + { + // Turn into just an emerald, and make it collectible! + ufo->health = 1; + ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); + return true; + } + + ufo->health -= damage; + K_SetHitLagForObjects(ufo, inflictor, damage * 6, true); + return true; } static mobj_t *InitSpecialUFO(waypoint_t *start) { mobj_t *ufo = NULL; + mobj_t *underlay = NULL; if (start == NULL) { @@ -343,6 +417,13 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) ufo_speed(ufo) = UFO_BASE_SPEED; + ufo->color = SKINCOLOR_CHAOSEMERALD1; + + underlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&underlay->target, ufo); + P_SetMobjState(underlay, S_CHAOSEMERALD_UNDER); + underlay->color = ufo->color; + return ufo; } diff --git a/src/p_inter.c b/src/p_inter.c index e77a5d8ff..c18a151aa 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2222,6 +2222,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } else { + if (target->type == MT_SPECIAL_UFO) + { + return Obj_SpecialUFODamage(target, inflictor, source, damagetype); + } + if (damagetype & DMG_STEAL) { // Not a player, steal damage is intended to not do anything From 2a0926bff4ed1da956f8b7593adf9ade57123975 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 22:11:18 -0500 Subject: [PATCH 04/40] Next round of UFO tweaks - Damage depends on item thrown at it. - UFO speeds up when getting damaged. - Made UFO speed up more often. - First Special Stage item column is active farther back. - Items can't be disabled if rules can't be changed. - Item column code with items disabled uses fractional precision. - Fixed bug with the new item table code to support different lengths better. - Tweaked UFO speed values again. - UFO is no longer solid. --- src/info.c | 2 +- src/k_kart.c | 103 ++++++++++++++++++++++++++++------------------ src/k_kart.h | 1 + src/objects/ufo.c | 57 +++++++++++++++++-------- 4 files changed, 104 insertions(+), 59 deletions(-) diff --git a/src/info.c b/src/info.c index 3faf15dd0..a80f67a78 100644 --- a/src/info.c +++ b/src/info.c @@ -29014,7 +29014,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 16, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SHOOTABLE|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, }; diff --git a/src/k_kart.c b/src/k_kart.c index ca030f88a..e3d0dd9ef 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -651,6 +651,24 @@ void K_RunItemCooldowns(void) } } +boolean K_ItemEnabled(SINT8 item) +{ + if (item < 1 || item >= NUMKARTRESULTS) + { + // Not a real item. + return false; + } + + if (K_CanChangeRules(true) == false) + { + // Force all items to be enabled. + return true; + } + + // Allow the user preference. + return KartItemCVars[item - 1]->value; +} + /** \brief Item Roulette for Kart @@ -760,7 +778,7 @@ INT32 K_KartGetItemOdds( I_Assert(item > KITEM_NONE); // too many off by one scenarioes. I_Assert(KartItemCVars[NUMKARTRESULTS-2] != NULL); // Make sure this exists - if (!KartItemCVars[item-1]->value && !modeattacking) + if (K_ItemEnabled(item) == false) { return 0; } @@ -837,32 +855,38 @@ INT32 K_KartGetItemOdds( } } - if (players[i].position == 1) + if (specialStage.active == false) { - first = &players[i]; - } + if (players[i].position == 1) + { + first = &players[i]; + } - if (players[i].position == 2) - { - second = &players[i]; + if (players[i].position == 2) + { + second = &players[i]; + } } } - if (first != NULL) // calculate 2nd's distance from 1st, for SPB + if (specialStage.active == false) { - firstDist = first->distancetofinish; - isFirst = (ourDist <= firstDist); - } + if (first != NULL) // calculate 2nd's distance from 1st, for SPB + { + firstDist = first->distancetofinish; + isFirst = (ourDist <= firstDist); + } - if (second != NULL) - { - secondDist = second->distancetofinish; - } + if (second != NULL) + { + secondDist = second->distancetofinish; + } - if (first != NULL && second != NULL) - { - secondToFirst = secondDist - firstDist; - secondToFirst = K_ScaleItemDistance(secondToFirst, 16 - pingame); // Reversed scaling, so 16P is like 1v1, and 1v1 is like 16P + if (first != NULL && second != NULL) + { + secondToFirst = secondDist - firstDist; + secondToFirst = K_ScaleItemDistance(secondToFirst, 16 - pingame); // Reversed scaling, so 16P is like 1v1, and 1v1 is like 16P + } } switch (item) @@ -1050,7 +1074,7 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum } #define SETUPDISTTABLE(odds, num) \ - totalsize++;\ + totalsize += num;\ if (oddsvalid[odds]) \ for (i = num; i; --i) \ disttable[distlen++] = odds; @@ -1075,11 +1099,9 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum } else { - UINT32 itotaldis = 0; - if (specialStage.active == true) // Special Stages { - SETUPDISTTABLE(0,1); + SETUPDISTTABLE(0,2); SETUPDISTTABLE(1,2); SETUPDISTTABLE(2,3); SETUPDISTTABLE(3,1); @@ -1096,25 +1118,26 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum SETUPDISTTABLE(7,1); } - itotaldis = DISTVAR * (((totalsize - 2) * distlen) / totalsize); + for (i = 0; i < totalsize; i++) + { + fixed_t pos = 0; + fixed_t dist = 0; + UINT8 index = 0; - if (pdis == 0) - { - useodds = disttable[0]; - } - else if (pdis > itotaldis) - { - useodds = disttable[distlen-1]; - } - else - { - for (i = 1; i < totalsize-1; i++) + if (i == totalsize-1) { - if (pdis <= DISTVAR * ((i * distlen) / totalsize)) - { - useodds = disttable[((i * distlen) / totalsize)]; - break; - } + useodds = disttable[distlen-1]; + break; + } + + pos = ((i << FRACBITS) * distlen) / totalsize; + dist = FixedMul(DISTVAR << FRACBITS, pos) >> FRACBITS; + index = FixedInt(FixedRound(pos)); + + if (pdis <= (unsigned)dist) + { + useodds = disttable[index]; + break; } } } diff --git a/src/k_kart.h b/src/k_kart.h index 8f43a6f37..167572260 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -62,6 +62,7 @@ UINT8 K_ItemResultToAmount(SINT8 getitem); tic_t K_GetItemCooldown(SINT8 itemResult); void K_SetItemCooldown(SINT8 itemResult, tic_t time); void K_RunItemCooldowns(void); +boolean K_ItemEnabled(SINT8 item); fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against); boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2); boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj); diff --git a/src/objects/ufo.c b/src/objects/ufo.c index fa26ef51d..eba92c525 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -24,12 +24,12 @@ #include "../k_waypoint.h" #include "../k_specialstage.h" -#define UFO_BASE_SPEED (16 * FRACUNIT) // UFO's slowest speed. -#define UFO_SPEEDUP (FRACUNIT >> 3) -#define UFO_SLOWDOWN (FRACUNIT >> 2) -#define UFO_SPACING (1024 * FRACUNIT) -#define UFO_DEADZONE (768 * FRACUNIT) -#define UFO_SPEEDFACTOR (FRACUNIT * 9 / 10) +#define UFO_BASE_SPEED (24 * FRACUNIT) // UFO's slowest speed. +#define UFO_SPEEDUP (FRACUNIT >> 1) // Acceleration +#define UFO_SLOWDOWN (FRACUNIT >> 1) // Deceleration +#define UFO_SPACING (1024 * FRACUNIT) // How far the UFO wants to stay in front +#define UFO_DEADZONE (2048 * FRACUNIT) // Deadzone where it won't update it's speed as much. +#define UFO_SPEEDFACTOR (FRACUNIT * 9 / 10) // Factor of player's best speed, to make it more fair. #define ufo_waypoint(o) ((o)->extravalue1) #define ufo_distancetofinish(o) ((o)->extravalue2) @@ -157,17 +157,17 @@ static void UFOUpdateSpeed(mobj_t *ufo) distDelta = ufo_distancetofinish(ufo) - wantedDist; - if (abs(distDelta) <= deadzone) + if (distDelta > 0) { - // We're in a good spot, try to match the player. - wantedSpeed = max(bestSpeed >> 1, baseSpeed); + // Too far behind! Start speeding up! + wantedSpeed = max(bestSpeed << 1, baseSpeed << 2); } else { - if (distDelta > 0) + if (abs(distDelta) < deadzone) { - // Too far behind! Start speeding up! - wantedSpeed = max(bestSpeed << 1, baseSpeed << 2); + // We're in a good spot, try to match the player. + wantedSpeed = max(bestSpeed >> 1, baseSpeed); } else { @@ -354,14 +354,29 @@ static UINT8 GetUFODamage(mobj_t *inflictor) { if (inflictor == NULL || P_MobjWasRemoved(inflictor) == true) { - return 1; + // Default damage value. + return 10; } switch (inflictor->type) { + case MT_SPB: + { + // SPB deals triple damage. + return 30; + } + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + { + // Orbinauts deal double damage. + return 20; + } + case MT_JAWZ: + case MT_JAWZ_SHIELD: default: { - return 1; + // Jawz deal minimal damage. + return 10; } } } @@ -373,6 +388,9 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN (void)source; (void)damageType; + // Speed up on damage! + ufo_speed(ufo) += FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed)); + if (UFOEmeraldChase(ufo) == true) { // Damaged fully already, no need for any more. @@ -384,14 +402,14 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN if (damage >= ufo->health - 1) { - // Turn into just an emerald, and make it collectible! + // Destroy the UFO parts, and make the emerald collectible! ufo->health = 1; ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); return true; } ufo->health -= damage; - K_SetHitLagForObjects(ufo, inflictor, damage * 6, true); + K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true); return true; } @@ -415,15 +433,18 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) UFOUpdateDistanceToFinish(ufo); } - ufo_speed(ufo) = UFO_BASE_SPEED; + ufo_speed(ufo) = FixedMul(UFO_BASE_SPEED << 2, K_GetKartGameSpeedScalar(gamespeed)); + // TODO: Adjustable Special Stage emerald color ufo->color = SKINCOLOR_CHAOSEMERALD1; underlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); P_SetTarget(&underlay->target, ufo); - P_SetMobjState(underlay, S_CHAOSEMERALD_UNDER); underlay->color = ufo->color; + // TODO: Super Emeralds / Chaos Rings + P_SetMobjState(underlay, S_CHAOSEMERALD_UNDER); + return ufo; } From 0cb2f11b13cbf640ee0c0de06992e773240f81f2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 22:46:45 -0500 Subject: [PATCH 05/40] Set eventmode when testing Special Stages from ZB --- src/d_main.c | 12 ++++++++++++ src/p_mobj.c | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 78741c437..c760d2ad0 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1849,6 +1849,18 @@ void D_SRB2Main(void) G_SetUsedCheats(); } + if (grandprixinfo.gp == true && mapheaderinfo[pstartmap-1]) + { + if (mapheaderinfo[pstartmap-1]->typeoflevel & TOL_SPECIAL) + { + specialStage.active = true; + specialStage.encore = grandprixinfo.encore; + grandprixinfo.eventmode = GPEVENT_SPECIAL; + } + + G_SetUsedCheats(); + } + D_MapChange(pstartmap, gametype, (cv_kartencore.value == 1), true, 0, false, false); } } diff --git a/src/p_mobj.c b/src/p_mobj.c index d2ec29404..147734252 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -46,6 +46,7 @@ #include "k_terrain.h" #include "k_collide.h" #include "k_objects.h" +#include "k_grandprix.h" static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); @@ -11305,13 +11306,12 @@ void P_SpawnPlayer(INT32 playernum) } else if (p->bot) { - /* - if (bonusgame || specialstage || boss) + if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE) { - // Bots should avoid + // Bots aren't supposed to be here. p->spectator = true; } - */ + else { // No point in a spectating bot! p->spectator = false; From 472bde4c23bc4cc75d90912eeb8d7534f3feb914 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 22:54:57 -0500 Subject: [PATCH 06/40] Add prints for winning / losing for now. --- src/objects/ufo.c | 24 +++++++++++++++++++----- src/p_inter.c | 6 ++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index eba92c525..f3a1fb803 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -227,7 +227,13 @@ static void UFOMove(mobj_t *ufo) path_t pathtofinish = {0}; size_t pathIndex = 0; - curWaypoint = K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo)); + boolean reachedEnd = false; + + if (ufo_waypoint(ufo) >= 0) + { + curWaypoint = K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo)); + } + destWaypoint = K_GetFinishLineWaypoint(); if (curWaypoint == NULL || destWaypoint == NULL) @@ -275,6 +281,7 @@ static void UFOMove(mobj_t *ufo) if (curWaypoint == destWaypoint) { // Reached the end. + reachedEnd = true; break; } @@ -292,7 +299,7 @@ static void UFOMove(mobj_t *ufo) if (pathfindsuccess == false) { // Path isn't valid. - // Just transition into the next state. + // Just keep going. break; } } @@ -302,6 +309,7 @@ static void UFOMove(mobj_t *ufo) if (pathIndex >= pathtofinish.numnodes) { // Successfully reached the end of the path. + reachedEnd = true; break; } @@ -313,6 +321,12 @@ static void UFOMove(mobj_t *ufo) UFOMoveTo(ufo, newX, newY, newZ); + if (reachedEnd == true) + { + CONS_Printf("You lost...\n"); + ufo_waypoint(ufo) = -1; // Invalidate + } + if (pathfindsuccess == true) { Z_Free(pathtofinish.array); @@ -366,16 +380,16 @@ static UINT8 GetUFODamage(mobj_t *inflictor) return 30; } case MT_ORBINAUT: - case MT_ORBINAUT_SHIELD: { - // Orbinauts deal double damage. + // Thrown orbinauts deal double damage. return 20; } case MT_JAWZ: case MT_JAWZ_SHIELD: + case MT_ORBINAUT_SHIELD: default: { - // Jawz deal minimal damage. + // Jawz / shields deal regular damage. return 10; } } diff --git a/src/p_inter.c b/src/p_inter.c index c18a151aa..0b00ed517 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -374,6 +374,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) player->emeralds |= special->extravalue1; K_CheckEmeralds(player); break; + case MT_SPECIAL_UFO: + if (!P_CanPickupItem(player, 0)) + return; + + CONS_Printf("You win!\n"); + break; /* case MT_EERIEFOG: special->frame &= ~FF_TRANS80; From e71dcd5d7d9a946b525768c0c1ac2e6762a81efb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 23:33:40 -0500 Subject: [PATCH 07/40] Sneakers deal contact damage to the UFO --- src/k_kart.c | 4 ++++ src/k_objects.h | 1 + src/objects/ufo.c | 47 +++++++++++++++++++++++++++++++++++++++++------ src/p_inter.c | 6 ++++++ src/p_map.c | 8 ++++++++ 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index e3d0dd9ef..6a49cba68 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1575,6 +1575,10 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against) { weight = 0; // This player does not cause any bump action } + else if (against && against->type == MT_SPECIAL_UFO) + { + weight = 0; + } else { // Applies rubberbanding, to prevent rubberbanding bots diff --git a/src/k_objects.h b/src/k_objects.h index a8daaf50b..cf39ea278 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -57,6 +57,7 @@ void Obj_DuelBombInit(mobj_t *bomb); /* Special Stage UFO */ void Obj_SpecialUFOThinker(mobj_t *bomb); boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType); +void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other); mobj_t *Obj_CreateSpecialUFO(void); UINT32 K_GetSpecialUFODistance(void); diff --git a/src/objects/ufo.c b/src/objects/ufo.c index f3a1fb803..852eb4bba 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -27,13 +27,14 @@ #define UFO_BASE_SPEED (24 * FRACUNIT) // UFO's slowest speed. #define UFO_SPEEDUP (FRACUNIT >> 1) // Acceleration #define UFO_SLOWDOWN (FRACUNIT >> 1) // Deceleration -#define UFO_SPACING (1024 * FRACUNIT) // How far the UFO wants to stay in front +#define UFO_SPACING (768 * FRACUNIT) // How far the UFO wants to stay in front #define UFO_DEADZONE (2048 * FRACUNIT) // Deadzone where it won't update it's speed as much. -#define UFO_SPEEDFACTOR (FRACUNIT * 9 / 10) // Factor of player's best speed, to make it more fair. +#define UFO_SPEEDFACTOR (FRACUNIT * 3 / 4) // Factor of player's best speed, to make it more fair. #define ufo_waypoint(o) ((o)->extravalue1) #define ufo_distancetofinish(o) ((o)->extravalue2) #define ufo_speed(o) ((o)->watertop) +#define ufo_collectdelay(o) ((o)->threshold) static void UFOMoveTo(mobj_t *ufo, fixed_t destx, fixed_t desty, fixed_t destz) { @@ -131,7 +132,7 @@ static void UFOUpdateSpeed(mobj_t *ufo) // 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 = min(bestSpeed, K_GetKartSpeed(player, false, false)); // Don't become unfair with Sneakers. bestSpeed = FixedDiv(bestSpeed, mapobjectscale); // Unscale from mapobjectscale to FRACUNIT bestSpeed = FixedMul(bestSpeed, UFO_SPEEDFACTOR); // Make it a bit more lenient } @@ -160,11 +161,11 @@ static void UFOUpdateSpeed(mobj_t *ufo) if (distDelta > 0) { // Too far behind! Start speeding up! - wantedSpeed = max(bestSpeed << 1, baseSpeed << 2); + wantedSpeed = max(bestSpeed, baseSpeed << 2); } else { - if (abs(distDelta) < deadzone) + if (abs(distDelta) <= deadzone) { // We're in a good spot, try to match the player. wantedSpeed = max(bestSpeed >> 1, baseSpeed); @@ -361,6 +362,11 @@ void Obj_SpecialUFOThinker(mobj_t *ufo) { // Spawn emerald sparkles UFOEmeraldVFX(ufo); + ufo_collectdelay(ufo)--; + } + else + { + ufo_collectdelay(ufo) = TICRATE; } } @@ -379,6 +385,11 @@ static UINT8 GetUFODamage(mobj_t *inflictor) // SPB deals triple damage. return 30; } + case MT_PLAYER: + { + // Players deal damage relative to how many sneakers they used. + return 15 * inflictor->player->numsneakers; + } case MT_ORBINAUT: { // Thrown orbinauts deal double damage. @@ -414,6 +425,13 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN damage = GetUFODamage(inflictor); + if (damage <= 0) + { + return false; + } + + K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true); + if (damage >= ufo->health - 1) { // Destroy the UFO parts, and make the emerald collectible! @@ -423,10 +441,27 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN } ufo->health -= damage; - K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true); return true; } +void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other) +{ + if (other->player == NULL) + { + return; + } + + if ((other->player->sneakertimer > 0) + && !P_PlayerInPain(other->player) + && (other->player->flashing == 0)) + { + // Bump and deal damage. + Obj_SpecialUFODamage(ufo, other, other, DMG_STEAL); + K_KartBouncing(other, ufo); + other->player->sneakertimer = 0; + } +} + static mobj_t *InitSpecialUFO(waypoint_t *start) { mobj_t *ufo = NULL; diff --git a/src/p_inter.c b/src/p_inter.c index 0b00ed517..fc1b2fd55 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -378,6 +378,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!P_CanPickupItem(player, 0)) return; + if (special->threshold > 0) + return; + + if (toucher->hitlag > 0) + return; + CONS_Printf("You win!\n"); break; /* diff --git a/src/p_map.c b/src/p_map.c index 677954b43..09cbbcaf7 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1337,6 +1337,14 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) return BMIT_CONTINUE; } + else if (thing->type == MT_SPECIAL_UFO) + { + if (!(thing->flags & MF_SPECIAL)) + { + Obj_PlayerUFOCollide(thing, tmthing); + return BMIT_CONTINUE; + } + } else if (thing->type == MT_BLUEROBRA_HEAD || thing->type == MT_BLUEROBRA_JOINT) { // see if it went over / under From d24be71909c6a78e8b4eac792fbd4d776ff1958a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 23:35:20 -0500 Subject: [PATCH 08/40] Prevent possible overflow, just in case --- src/objects/ufo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 852eb4bba..7cb15ff27 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -506,7 +506,7 @@ mobj_t *Obj_CreateSpecialUFO(void) { const boolean huntbackwards = true; const boolean useshortcuts = false; - const UINT32 traveldist = UINT32_MAX; // Go as far back as possible. + const UINT32 traveldist = INT32_MAX; // Go as far back as possible. Not UINT32_MAX to avoid possible overflow. boolean pathfindsuccess = false; path_t pathtofinish = {0}; From 1317ce5a3f11b46ab4180cefe9f782f10b11ed03 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 27 Nov 2022 08:16:18 -0500 Subject: [PATCH 09/40] First pass on UFO visuals --- src/deh_tables.c | 6 ++ src/info.c | 38 +++++++- src/info.h | 10 ++ src/k_objects.h | 5 +- src/objects/ufo.c | 236 +++++++++++++++++++++++++++++++++++++++++++--- src/p_map.c | 2 +- src/p_mobj.c | 31 +++++- 7 files changed, 312 insertions(+), 16 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index c89b4e0f6..bec79cb3a 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4529,6 +4529,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_JANKSPARK2", "S_JANKSPARK3", "S_JANKSPARK4", + + "S_SPECIAL_UFO_POD", + "S_SPECIAL_UFO_OVERLAY", + "S_SPECIAL_UFO_ARM", + "S_SPECIAL_UFO_STEM", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -5624,6 +5629,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BEAMPOINT", "MT_SPECIAL_UFO", + "MT_SPECIAL_UFO_PIECE", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index 28ba369f5..530280b60 100644 --- a/src/info.c +++ b/src/info.c @@ -783,6 +783,10 @@ char sprnames[NUMSPRITES + 1][5] = "FLBM", + "UFOB", + "UFOA", + "UFOS", + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later "VIEW", }; @@ -5140,6 +5144,11 @@ state_t states[NUMSTATES] = {SPR_JANK, FF_PAPERSPRITE|FF_FULLBRIGHT|FF_ANIMATE, 4, {NULL}, 3, 1, S_JANKSPARK3}, // S_JANKSPARK2 {SPR_JANK, 0, 0, {A_SetCustomValue}, -1, 5, S_JANKSPARK4}, // S_JANKSPARK3 {SPR_JANK, 0, 0, {A_ChangeAngleRelative}, 180, 180, S_JANKSPARK2}, // S_JANKSPARK4 + + {SPR_UFOB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_POD + {SPR_UFOB, 1|FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 1, 1, S_NULL}, // S_SPECIAL_UFO_OVERLAY + {SPR_UFOA, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_ARM + {SPR_UFOS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_STEM }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -29042,7 +29051,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 72*FRACUNIT, // radius + 108*FRACUNIT, // radius 72*FRACUNIT, // height 0, // display offset 16, // mass @@ -29051,6 +29060,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_SHOOTABLE|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, + + { // MT_SPECIAL_UFO_PIECE + -1, // doomednum + S_INVISIBLE, // 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 + 8*FRACUNIT, // radius + 16*FRACUNIT, // height + 1, // display offset + 100, // mass + 1, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, }; skincolor_t skincolors[MAXSKINCOLORS] = { diff --git a/src/info.h b/src/info.h index 6ff3fefd4..cd7c9d289 100644 --- a/src/info.h +++ b/src/info.h @@ -1329,6 +1329,10 @@ typedef enum sprite SPR_FLBM, // Finish line beam + SPR_UFOB, + SPR_UFOA, + SPR_UFOS, + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later SPR_VIEW, @@ -5562,6 +5566,11 @@ typedef enum state S_JANKSPARK3, S_JANKSPARK4, + S_SPECIAL_UFO_POD, + S_SPECIAL_UFO_OVERLAY, + S_SPECIAL_UFO_ARM, + S_SPECIAL_UFO_STEM, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -6676,6 +6685,7 @@ typedef enum mobj_type MT_BEAMPOINT, MT_SPECIAL_UFO, + MT_SPECIAL_UFO_PIECE, MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, diff --git a/src/k_objects.h b/src/k_objects.h index cf39ea278..921d587a7 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -55,9 +55,12 @@ void Obj_DuelBombTouch(mobj_t *bomb, mobj_t *toucher); void Obj_DuelBombInit(mobj_t *bomb); /* Special Stage UFO */ -void Obj_SpecialUFOThinker(mobj_t *bomb); +void Obj_SpecialUFOThinker(mobj_t *ufo); boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType); void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other); +void Obj_UFOPieceThink(mobj_t *piece); +void Obj_UFOPieceDead(mobj_t *piece); +void Obj_UFOPieceRemoved(mobj_t *piece); mobj_t *Obj_CreateSpecialUFO(void); UINT32 K_GetSpecialUFODistance(void); diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 7cb15ff27..9672d840e 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -8,7 +8,7 @@ // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file ufo.c -/// \brief Special Stage UFO +/// \brief Special Stage UFO + Emerald handler #include "../doomdef.h" #include "../doomstat.h" @@ -31,11 +31,29 @@ #define UFO_DEADZONE (2048 * FRACUNIT) // Deadzone where it won't update it's speed as much. #define UFO_SPEEDFACTOR (FRACUNIT * 3 / 4) // Factor of player's best speed, to make it more fair. +#define UFO_NUMARMS (3) +#define UFO_ARMDELTA (ANGLE_MAX / UFO_NUMARMS) + #define ufo_waypoint(o) ((o)->extravalue1) #define ufo_distancetofinish(o) ((o)->extravalue2) #define ufo_speed(o) ((o)->watertop) #define ufo_collectdelay(o) ((o)->threshold) +#define ufo_pieces(o) ((o)->hnext) + +#define ufo_piece_type(o) ((o)->extravalue1) + +#define ufo_piece_owner(o) ((o)->target) +#define ufo_piece_next(o) ((o)->hnext) +#define ufo_piece_prev(o) ((o)->hprev) + +enum +{ + UFO_PIECE_TYPE_POD, + UFO_PIECE_TYPE_ARM, + UFO_PIECE_TYPE_STEM, +}; + static void UFOMoveTo(mobj_t *ufo, fixed_t destx, fixed_t desty, fixed_t destz) { ufo->momx = destx - ufo->x; @@ -55,6 +73,11 @@ static boolean UFOEmeraldChase(mobj_t *ufo) return (ufo->health <= 1); } +static boolean UFOPieceValid(mobj_t *piece) +{ + return (piece != NULL && P_MobjWasRemoved(piece) == false && piece->health > 0); +} + static void UFOUpdateDistanceToFinish(mobj_t *ufo) { waypoint_t *finishLine = K_GetFinishLineWaypoint(); @@ -221,6 +244,7 @@ static void UFOMove(mobj_t *ufo) fixed_t newX = ufo->x; fixed_t newY = ufo->y; fixed_t newZ = ufo->z; + const fixed_t floatHeight = 24 * ufo->scale; const boolean useshortcuts = false; const boolean huntbackwards = false; @@ -253,7 +277,7 @@ static void UFOMove(mobj_t *ufo) { fixed_t wpX = curWaypoint->mobj->x; fixed_t wpY = curWaypoint->mobj->y; - fixed_t wpZ = curWaypoint->mobj->z; + fixed_t wpZ = curWaypoint->mobj->z + floatHeight; fixed_t distToNext = GenericDistance( newX, newY, newZ, @@ -336,13 +360,19 @@ static void UFOMove(mobj_t *ufo) static void UFOEmeraldVFX(mobj_t *ufo) { + const INT32 bobS = 32; + const angle_t bobA = (leveltime & (bobS - 1)) * (ANGLE_MAX / bobS); + const fixed_t bobH = 16 * ufo->scale; + + ufo->sprzoff = FixedMul(bobH, FINESINE(bobA >> ANGLETOFINESHIFT)); + if (leveltime % 3 == 0) { mobj_t *sparkle = P_SpawnMobjFromMobj( ufo, P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT, P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT, - P_RandomRange(PR_SPARKLE, 0, 64) * FRACUNIT, + (P_RandomRange(PR_SPARKLE, 0, 64) * FRACUNIT) + FixedDiv(ufo->sprzoff, ufo->scale), MT_EMERALDSPARK ); @@ -370,6 +400,65 @@ void Obj_SpecialUFOThinker(mobj_t *ufo) } } +static void UFOCopyHitlagToPieces(mobj_t *ufo) +{ + mobj_t *piece = NULL; + + piece = ufo_pieces(ufo); + while (UFOPieceValid(piece) == true) + { + piece->hitlag = ufo->hitlag; + piece->eflags = (piece->eflags & ~MFE_DAMAGEHITLAG) | (ufo->eflags & MFE_DAMAGEHITLAG); + piece = ufo_piece_next(piece); + } +} + +static void UFOKillPiece(mobj_t *piece) +{ + angle_t dir = ANGLE_MAX; + fixed_t thrust = 0; + + if (UFOPieceValid(piece) == false) + { + return; + } + + piece->health = 0; + piece->tics = TICRATE; + piece->flags &= ~MF_NOGRAVITY; + + switch (ufo_piece_type(piece)) + { + case UFO_PIECE_TYPE_ARM: + { + dir = piece->angle; + thrust = 12 * piece->scale; + break; + } + default: + { + dir = FixedAngle(P_RandomRange(PR_DECORATION, 0, 359) << FRACBITS); + thrust = 4 * piece->scale; + break; + } + } + + P_Thrust(piece, dir, -thrust); + P_SetObjectMomZ(piece, 12*FRACUNIT, true); +} + +static void UFOKillPieces(mobj_t *ufo) +{ + mobj_t *piece = NULL; + + piece = ufo_pieces(ufo); + while (UFOPieceValid(piece) == true) + { + UFOKillPiece(piece); + piece = ufo_piece_next(piece); + } +} + static UINT8 GetUFODamage(mobj_t *inflictor) { if (inflictor == NULL || P_MobjWasRemoved(inflictor) == true) @@ -408,18 +497,15 @@ static UINT8 GetUFODamage(mobj_t *inflictor) boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType) { + const fixed_t addSpeed = FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed)); UINT8 damage = 1; (void)source; (void)damageType; - // Speed up on damage! - ufo_speed(ufo) += FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed)); - if (UFOEmeraldChase(ufo) == true) { // Damaged fully already, no need for any more. - ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); // Double check flags, just to be sure. return false; } @@ -430,13 +516,22 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN return false; } + // Speed up on damage! + ufo_speed(ufo) += addSpeed; + K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true); + UFOCopyHitlagToPieces(ufo); if (damage >= ufo->health - 1) { // Destroy the UFO parts, and make the emerald collectible! + UFOKillPieces(ufo); + ufo->health = 1; ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); + ufo->shadowscale = FRACUNIT/3; + + ufo_speed(ufo) += addSpeed; // Even more speed! return true; } @@ -462,10 +557,96 @@ void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other) } } +void Obj_UFOPieceThink(mobj_t *piece) +{ + mobj_t *ufo = ufo_piece_owner(piece); + + if (ufo == NULL || P_MobjWasRemoved(ufo) == true) + { + P_KillMobj(piece, NULL, NULL, DMG_NORMAL); + return; + } + + piece->destscale = ufo->destscale; + piece->scalespeed = ufo->scalespeed; + + switch (ufo_piece_type(piece)) + { + case UFO_PIECE_TYPE_POD: + { + UFOMoveTo(piece, ufo->x, ufo->y, ufo->z + (120 * ufo->scale)); + break; + } + case UFO_PIECE_TYPE_ARM: + { + fixed_t dis = (104 * ufo->scale); + + fixed_t x = ufo->x - FixedMul(dis, FINECOSINE(piece->angle >> ANGLETOFINESHIFT)); + fixed_t y = ufo->y - FixedMul(dis, FINESINE(piece->angle >> ANGLETOFINESHIFT)); + + UFOMoveTo(piece, x, y, ufo->z + (24 * ufo->scale)); + + piece->angle -= FixedMul(ANG2, FixedDiv(ufo_speed(ufo), UFO_BASE_SPEED)); + break; + } + default: + { + P_KillMobj(piece, NULL, NULL, DMG_NORMAL); + return; + } + } +} + +void Obj_UFOPieceDead(mobj_t *piece) +{ + piece->renderflags ^= RF_DONTDRAW; +} + +void Obj_UFOPieceRemoved(mobj_t *piece) +{ + // Repair piece list. + mobj_t *ufo = ufo_piece_owner(piece); + mobj_t *next = ufo_piece_next(piece); + mobj_t *prev = ufo_piece_prev(piece); + + if (prev != NULL && P_MobjWasRemoved(prev) == false) + { + P_SetTarget( + &ufo_piece_next(prev), + (next != NULL && P_MobjWasRemoved(next) == false) ? next : NULL + ); + } + + if (next != NULL && P_MobjWasRemoved(next) == false) + { + P_SetTarget( + &ufo_piece_prev(next), + (prev != NULL && P_MobjWasRemoved(prev) == false) ? prev : NULL + ); + + if (ufo != NULL && P_MobjWasRemoved(ufo) == false) + { + if (piece == ufo_pieces(ufo)) + { + P_SetTarget( + &ufo_pieces(ufo), + next + ); + } + } + } + + P_SetTarget(&ufo_piece_next(piece), NULL); + P_SetTarget(&ufo_piece_prev(piece), NULL); +} + static mobj_t *InitSpecialUFO(waypoint_t *start) { mobj_t *ufo = NULL; - mobj_t *underlay = NULL; + mobj_t *overlay = NULL; + mobj_t *piece = NULL; + mobj_t *prevPiece = NULL; + size_t i; if (start == NULL) { @@ -487,12 +668,43 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) // TODO: Adjustable Special Stage emerald color ufo->color = SKINCOLOR_CHAOSEMERALD1; - underlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); - P_SetTarget(&underlay->target, ufo); - underlay->color = ufo->color; + overlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&overlay->target, ufo); + overlay->color = ufo->color; // TODO: Super Emeralds / Chaos Rings - P_SetMobjState(underlay, S_CHAOSEMERALD_UNDER); + P_SetMobjState(overlay, S_CHAOSEMERALD_UNDER); + + // Create UFO pieces. + // First: UFO center. + piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); + P_SetTarget(&ufo_piece_owner(piece), ufo); + + P_SetMobjState(piece, S_SPECIAL_UFO_POD); + ufo_piece_type(piece) = UFO_PIECE_TYPE_POD; + + overlay = P_SpawnMobjFromMobj(piece, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&overlay->target, piece); + P_SetMobjState(overlay, S_SPECIAL_UFO_OVERLAY); + + P_SetTarget(&ufo_pieces(ufo), piece); + prevPiece = piece; + + for (i = 0; i < UFO_NUMARMS; i++) + { + piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); + P_SetTarget(&ufo_piece_owner(piece), ufo); + + P_SetMobjState(piece, S_SPECIAL_UFO_ARM); + ufo_piece_type(piece) = UFO_PIECE_TYPE_ARM; + + piece->angle = UFO_ARMDELTA * i; + + P_SetTarget(&ufo_piece_next(prevPiece), piece); + P_SetTarget(&ufo_piece_prev(piece), prevPiece); + + prevPiece = piece; + } return ufo; } diff --git a/src/p_map.c b/src/p_map.c index 75781e058..e28a9c64f 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1352,7 +1352,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) { if (!(thing->flags & MF_SPECIAL)) { - Obj_PlayerUFOCollide(thing, tmthing); + Obj_PlayerUFOCollide(thing, tm.thing); return BMIT_CONTINUE; } } diff --git a/src/p_mobj.c b/src/p_mobj.c index bebe706a8..e8c0cd60b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5298,10 +5298,22 @@ void P_RunOverlays(void) mo->pitch = mo->target->pitch; mo->roll = mo->target->roll; + mo->spritexoffset = mo->target->spritexoffset; + mo->spriteyoffset = mo->target->spriteyoffset; + mo->spritexscale = mo->target->spritexscale; + mo->spriteyscale = mo->target->spriteyscale; + + mo->sprxoff = mo->target->sprxoff; + mo->spryoff = mo->target->spryoff; + mo->sprzoff = mo->target->sprzoff; + + mo->hitlag = mo->target->hitlag; + mo->eflags = (mo->eflags & ~MFE_DAMAGEHITLAG) | (mo->target->eflags & MFE_DAMAGEHITLAG); + if ((mo->flags & MF_DONTENCOREMAP) != (mo->target->flags & MF_DONTENCOREMAP)) mo->flags ^= MF_DONTENCOREMAP; - mo->dispoffset = mo->target->dispoffset + mo->info->dispoffset; + mo->dispoffset = mo->target->dispoffset; if (!(mo->state->frame & FF_ANIMATE)) { @@ -5321,6 +5333,7 @@ void P_RunOverlays(void) // if you're using FF_ANIMATE on an overlay, // then you're on your own. zoffs = 0; + mo->dispoffset++; } P_UnsetThingPosition(mo); @@ -6633,6 +6646,11 @@ static boolean P_MobjDeadThink(mobj_t *mobj) S_StartSound(dust, sfx_s3k3d); } break; + case MT_SPECIAL_UFO_PIECE: + { + Obj_UFOPieceDead(mobj); + break; + } default: break; } @@ -7187,6 +7205,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_SpecialUFOThinker(mobj); break; } + case MT_SPECIAL_UFO_PIECE: + { + Obj_UFOPieceThink(mobj); + break; + } case MT_EMERALD: { if (battleovertime.enabled >= 10*TICRATE) @@ -10002,6 +10025,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) { case MT_PLAYER: case MT_KART_LEFTOVER: + case MT_SPECIAL_UFO: thing->shadowscale = FRACUNIT; break; case MT_SMALLMACE: @@ -10858,6 +10882,11 @@ void P_RemoveMobj(mobj_t *mobj) Obj_ShrinkGunRemoved(mobj); } + if (mobj->type == MT_SPECIAL_UFO_PIECE) + { + Obj_UFOPieceRemoved(mobj); + } + mobj->health = 0; // Just because // unlink from sector and block lists From 56652c035e0312f90aaef1bf38a3f7e5b1afc132 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 27 Nov 2022 19:09:30 -0500 Subject: [PATCH 10/40] Adjust UFO pieces --- src/objects/ufo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 9672d840e..99feb055e 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -567,24 +567,24 @@ void Obj_UFOPieceThink(mobj_t *piece) return; } - piece->destscale = ufo->destscale; + piece->destscale = 3 * ufo->destscale / 2; piece->scalespeed = ufo->scalespeed; switch (ufo_piece_type(piece)) { case UFO_PIECE_TYPE_POD: { - UFOMoveTo(piece, ufo->x, ufo->y, ufo->z + (120 * ufo->scale)); + UFOMoveTo(piece, ufo->x, ufo->y, ufo->z + (132 * piece->scale)); break; } case UFO_PIECE_TYPE_ARM: { - fixed_t dis = (104 * ufo->scale); + fixed_t dis = (88 * piece->scale); fixed_t x = ufo->x - FixedMul(dis, FINECOSINE(piece->angle >> ANGLETOFINESHIFT)); fixed_t y = ufo->y - FixedMul(dis, FINESINE(piece->angle >> ANGLETOFINESHIFT)); - UFOMoveTo(piece, x, y, ufo->z + (24 * ufo->scale)); + UFOMoveTo(piece, x, y, ufo->z + (24 * piece->scale)); piece->angle -= FixedMul(ANG2, FixedDiv(ufo_speed(ufo), UFO_BASE_SPEED)); break; From f986cf48ad56bde133602702b6fb6cf229366393 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 27 Nov 2022 19:37:32 -0500 Subject: [PATCH 11/40] Add stem --- src/info.c | 2 +- src/objects/ufo.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/info.c b/src/info.c index 530280b60..dddb9da97 100644 --- a/src/info.c +++ b/src/info.c @@ -29084,7 +29084,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_NOSQUISH, // flags S_NULL // raisestate }, }; diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 99feb055e..b9b8c2def 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -429,6 +429,11 @@ static void UFOKillPiece(mobj_t *piece) switch (ufo_piece_type(piece)) { + case UFO_PIECE_TYPE_STEM: + { + piece->tics = 1; + return; + } case UFO_PIECE_TYPE_ARM: { dir = piece->angle; @@ -589,9 +594,18 @@ void Obj_UFOPieceThink(mobj_t *piece) piece->angle -= FixedMul(ANG2, FixedDiv(ufo_speed(ufo), UFO_BASE_SPEED)); break; } + case UFO_PIECE_TYPE_STEM: + { + fixed_t stemZ = ufo->z + (294 * piece->scale); + fixed_t sc = FixedDiv(FixedDiv(ufo->ceilingz - stemZ, piece->scale), 15 * FRACUNIT); + + UFOMoveTo(piece, ufo->x, ufo->y, stemZ); + piece->spriteyscale = sc; + break; + } default: { - P_KillMobj(piece, NULL, NULL, DMG_NORMAL); + P_RemoveMobj(piece); return; } } @@ -690,6 +704,7 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) P_SetTarget(&ufo_pieces(ufo), piece); prevPiece = piece; + // Add the catcher arms. for (i = 0; i < UFO_NUMARMS; i++) { piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); @@ -702,10 +717,20 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) P_SetTarget(&ufo_piece_next(prevPiece), piece); P_SetTarget(&ufo_piece_prev(piece), prevPiece); - prevPiece = piece; } + // Add the stem. + piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); + P_SetTarget(&ufo_piece_owner(piece), ufo); + + P_SetMobjState(piece, S_SPECIAL_UFO_STEM); + ufo_piece_type(piece) = UFO_PIECE_TYPE_STEM; + + P_SetTarget(&ufo_piece_next(prevPiece), piece); + P_SetTarget(&ufo_piece_prev(piece), prevPiece); + prevPiece = piece; + return ufo; } From 0a733c726558afb8cc51cbc7f7757797535349e8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 21 Nov 2022 23:51:26 -0500 Subject: [PATCH 12/40] 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. --- src/deh_tables.c | 2 + src/info.c | 27 +++ src/info.h | 2 + src/k_kart.c | 260 ++++++++++++++++--------- src/k_objects.h | 4 + src/k_specialstage.c | 3 + src/k_specialstage.h | 2 +- src/k_waypoint.c | 59 ++++-- src/objects/CMakeLists.txt | 1 + src/objects/ufo.c | 375 +++++++++++++++++++++++++++++++++++++ src/p_mobj.c | 5 + 11 files changed, 630 insertions(+), 110 deletions(-) create mode 100644 src/objects/ufo.c diff --git a/src/deh_tables.c b/src/deh_tables.c index 9abc8f3f2..b60bf269e 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5629,6 +5629,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BEAMPOINT", "MT_BROLY", + + "MT_SPECIAL_UFO", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index 17d930a90..ea71b859a 100644 --- a/src/info.c +++ b/src/info.c @@ -29057,6 +29057,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|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] = { diff --git a/src/info.h b/src/info.h index bc074a0b5..fac8100c6 100644 --- a/src/info.h +++ b/src/info.h @@ -6684,6 +6684,8 @@ typedef enum mobj_type MT_BROLY, + MT_SPECIAL_UFO, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_kart.c b/src/k_kart.c index 92fb5dbc5..e2a70bdce 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -184,6 +184,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]]) @@ -1024,7 +1027,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 @@ -1055,9 +1058,9 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur c = FixedMul(CHAOTIXBANDCOLORS<> 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; @@ -1091,7 +1094,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; @@ -1116,6 +1119,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 @@ -1124,6 +1250,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; @@ -1161,104 +1290,43 @@ static void K_UpdateDraft(player_t *player) } // Not enough speed to draft. - if (player->speed >= 20*player->mo->scale) + 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; - } - - if (gametype == GT_BATTLE) - { - // TODO: gametyperules - // Double speed in Battle - player->draftpower += add; - } + continue; } - if (player->draftpower > FRACUNIT) - player->draftpower = FRACUNIT; + otherPlayer = &players[i]; - // Play draft finish noise - if (olddraft < FRACUNIT && player->draftpower >= FRACUNIT) - S_StartSound(player->mo, sfx_cdfm62); + if (otherPlayer->spectator == true) + { + continue; + } - // Spawn in the visual! - K_DrawDraftCombiring(player, &players[i], dist, draftdistance, false); + if (otherPlayer->mo == NULL || P_MobjWasRemoved(otherPlayer->mo) == true) + { + continue; + } - return; // Finished doing our draft. + if (K_TryDraft(player, otherPlayer->mo, minDist, draftdistance, leniency) == true) + { + return; // Finished doing our draft. + } } } @@ -1279,7 +1347,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. diff --git a/src/k_objects.h b/src/k_objects.h index fc15a2153..4ba2253c0 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -57,4 +57,8 @@ void Obj_DuelBombInit(mobj_t *bomb); /* Broly Ki */ mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration); +/* Special Stage UFO */ +void Obj_SpecialUFOThinker(mobj_t *bomb); +mobj_t *Obj_CreateSpecialUFO(void); + #endif/*k_objects_H*/ diff --git a/src/k_specialstage.c b/src/k_specialstage.c index 3a2d751ac..3700d98fb 100644 --- a/src/k_specialstage.c +++ b/src/k_specialstage.c @@ -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++) { diff --git a/src/k_specialstage.h b/src/k_specialstage.h index 8e11d761d..c93136b99 100644 --- a/src/k_specialstage.h +++ b/src/k_specialstage.h @@ -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; /*-------------------------------------------------- diff --git a/src/k_waypoint.c b/src/k_waypoint.c index a36cdfeb7..d53a4bbd3 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -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}; diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index b3b5ff2c0..00eadea36 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -9,4 +9,5 @@ target_sources(SRB2SDL2 PRIVATE jawz.c duel-bomb.c broly.c + ufo.c ) diff --git a/src/objects/ufo.c b/src/objects/ufo.c new file mode 100644 index 000000000..8b5dfaf6a --- /dev/null +++ b/src/objects/ufo.c @@ -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); +} diff --git a/src/p_mobj.c b/src/p_mobj.c index efddf4fb8..7aa9a6fb4 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7291,6 +7291,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) From dc6caf1eb39c57c8948a6fc1498395acb24782f8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 00:39:53 -0500 Subject: [PATCH 13/40] Implement the Special Stage item table --- src/k_objects.h | 1 + src/k_roulette.c | 112 +++++++++++++++++++++++++++++++++------------- src/objects/ufo.c | 14 ++++++ 3 files changed, 97 insertions(+), 30 deletions(-) diff --git a/src/k_objects.h b/src/k_objects.h index 4ba2253c0..d7abad6e5 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -60,5 +60,6 @@ mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration); /* Special Stage UFO */ void Obj_SpecialUFOThinker(mobj_t *bomb); mobj_t *Obj_CreateSpecialUFO(void); +UINT32 K_GetSpecialUFODistance(void); #endif/*k_objects_H*/ diff --git a/src/k_roulette.c b/src/k_roulette.c index ec15f4e9b..836aa29ba 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -108,7 +108,6 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] = { - //K L { 2, 1 }, // Sneaker { 0, 0 }, // Rocket Sneaker { 4, 1 }, // Invincibility @@ -139,6 +138,38 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] = { 5, 1 } // Jawz x2 }; +static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] = +{ + { 1, 1, 0, 0 }, // Sneaker + { 0, 0, 0, 0 }, // Rocket Sneaker + { 0, 0, 0, 0 }, // Invincibility + { 0, 0, 0, 0 }, // Banana + { 0, 0, 0, 0 }, // Eggman Monitor + { 1, 1, 0, 0 }, // Orbinaut + { 1, 1, 0, 0 }, // Jawz + { 0, 0, 0, 0 }, // Mine + { 0, 0, 0, 0 }, // Land Mine + { 0, 0, 0, 0 }, // Ballhog + { 0, 0, 0, 1 }, // Self-Propelled Bomb + { 0, 0, 0, 0 }, // Grow + { 0, 0, 0, 0 }, // Shrink + { 0, 0, 0, 0 }, // Lightning Shield + { 0, 0, 0, 0 }, // Bubble Shield + { 0, 0, 0, 0 }, // Flame Shield + { 0, 0, 0, 0 }, // Hyudoro + { 0, 0, 0, 0 }, // Pogo Spring + { 0, 0, 0, 0 }, // Super Ring + { 0, 0, 0, 0 }, // Kitchen Sink + { 0, 0, 0, 0 }, // Drop Target + { 0, 0, 0, 0 }, // Garden Top + { 0, 1, 1, 0 }, // Sneaker x2 + { 0, 0, 1, 1 }, // Sneaker x3 + { 0, 0, 0, 0 }, // Banana x3 + { 0, 1, 1, 0 }, // Orbinaut x3 + { 0, 0, 1, 1 }, // Orbinaut x4 + { 0, 0, 1, 1 } // Jawz x2 +}; + static kartitems_t K_KartItemReelTimeAttack[] = { KITEM_SNEAKER, @@ -321,7 +352,6 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers return 0; } -#if 0 if (specialStage.active == true) { UINT32 ufoDis = K_GetSpecialUFODistance(); @@ -338,7 +368,6 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers } } else -#endif { UINT8 i; for (i = 0; i < MAXPLAYERS; i++) @@ -470,10 +499,15 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table newOdds = K_KartItemOddsBattle[item-1][pos]; } + else if (specialStage.active == true) + { + I_Assert(pos < 4); // Ditto + newodds = K_KartItemOddsSpecial[item-1][pos]; + } else { I_Assert(pos < 8); // Ditto - newOdds = K_KartItemOddsRace[item-1][pos]; + newodds = K_KartItemOddsRace[item-1][pos]; } newOdds <<= FRACBITS; @@ -532,29 +566,32 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, return 0; } - if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done - || position == 1) // No SPB for 1st ever + if (specialStage.active == false) { - return 0; - } - else - { - const UINT32 dist = max(0, ((signed)roulette->secondToFirst) - SPBSTARTDIST); - const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST; - const fixed_t maxOdds = 20 << FRACBITS; - fixed_t multiplier = FixedDiv(dist, distRange); - - if (multiplier < 0) + if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done + || position == 1) // No SPB for 1st ever { - multiplier = 0; + return 0; } - - if (multiplier > FRACUNIT) + else { - multiplier = FRACUNIT; - } + const UINT32 dist = max(0, ((signed)roulette->secondToFirst) - SPBSTARTDIST); + const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST; + const fixed_t maxOdds = 20 << FRACBITS; + fixed_t multiplier = FixedDiv(dist, distRange); - newOdds = FixedMul(maxOdds, multiplier); + if (multiplier < 0) + { + multiplier = 0; + } + + if (multiplier > FRACUNIT) + { + multiplier = FRACUNIT; + } + + newOdds = FixedMul(maxOdds, multiplier); + } } break; } @@ -685,14 +722,24 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett } else { - SETUPDISTTABLE(0,1); - SETUPDISTTABLE(1,1); - SETUPDISTTABLE(2,1); - SETUPDISTTABLE(3,2); - SETUPDISTTABLE(4,2); - SETUPDISTTABLE(5,3); - SETUPDISTTABLE(6,3); - SETUPDISTTABLE(7,1); + if (specialStage.active == true) // Special Stages + { + SETUPDISTTABLE(0,1); + SETUPDISTTABLE(1,2); + SETUPDISTTABLE(2,3); + SETUPDISTTABLE(3,1); + } + else + { + SETUPDISTTABLE(0,1); + SETUPDISTTABLE(1,1); + SETUPDISTTABLE(2,1); + SETUPDISTTABLE(3,2); + SETUPDISTTABLE(4,2); + SETUPDISTTABLE(5,3); + SETUPDISTTABLE(6,3); + SETUPDISTTABLE(7,1); + } for (i = 0; i < totalSize; i++) { @@ -749,6 +796,11 @@ static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulett return false; } + if (specialStage.active == true) + { + return false; + } + if (player == NULL) { return false; diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 8b5dfaf6a..3d2ad68d7 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -22,6 +22,7 @@ #include "../g_game.h" #include "../z_zone.h" #include "../k_waypoint.h" +#include "../k_specialstage.h" #define UFO_BASE_SPEED (12 * FRACUNIT) // UFO's slowest speed. #define UFO_SPEEDUP (FRACUNIT) @@ -373,3 +374,16 @@ mobj_t *Obj_CreateSpecialUFO(void) return InitSpecialUFO(startWaypoint); } + +UINT32 K_GetSpecialUFODistance(void) +{ + if (specialStage.active == true) + { + if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false) + { + return (UINT32)ufo_distancetofinish(specialStage.ufo); + } + } + + return UINT32_MAX; +} From d1b2e425606cf01f1680a1d7bc16d09235462b9d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 16:31:30 -0500 Subject: [PATCH 14/40] Lots more UFO work - UFO is able to be damaged. (No damage values set yet, so it has an obnoxious amount of health.) - Emerald becomes collectible when fully damaged. - Jawz can target the UFO. - Tweaked some of the speed values. --- src/info.c | 2 +- src/k_kart.c | 63 +++++++++++++++++++++----------- src/k_kart.h | 2 +- src/k_objects.h | 1 + src/objects/jawz.c | 18 +++++---- src/objects/ufo.c | 91 +++++++++++++++++++++++++++++++++++++++++++--- src/p_inter.c | 5 +++ 7 files changed, 146 insertions(+), 36 deletions(-) diff --git a/src/info.c b/src/info.c index ea71b859a..a106464d7 100644 --- a/src/info.c +++ b/src/info.c @@ -29061,7 +29061,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_SPECIAL_UFO -1, // doomednum S_CHAOSEMERALD1, // spawnstate - 1000, // spawnhealth + 101, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime diff --git a/src/k_kart.c b/src/k_kart.c index e2a70bdce..86d177f90 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6716,12 +6716,18 @@ static void K_MoveHeldObjects(player_t *player) } } -player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) +mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) { fixed_t best = INT32_MAX; - player_t *wtarg = NULL; + mobj_t *wtarg = NULL; INT32 i; + if (specialStage.active == true) + { + // Always target the UFO. + return specialStage.ufo; + } + for (i = 0; i < MAXPLAYERS; i++) { angle_t thisang = ANGLE_MAX; @@ -6737,7 +6743,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) player = &players[i]; // Don't target yourself, stupid. - if (player == source) + if (source != NULL && player == source) { continue; } @@ -6776,7 +6782,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) if (gametyperules & GTR_CIRCUIT) { - if (player->position >= source->position) + if (source != NULL && player->position >= source->position) { // Don't pay attention to people who aren't above your position continue; @@ -6818,7 +6824,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) if (thisScore < best) { - wtarg = player; + wtarg = player->mo; best = thisScore; } } @@ -7944,24 +7950,32 @@ void K_KartPlayerAfterThink(player_t *player) // Jawz reticule (seeking) if (player->itemtype == KITEM_JAWZ && (player->pflags & PF_ITEMOUT)) { - INT32 lastTargID = player->lastjawztarget; - player_t *lastTarg = NULL; - player_t *targ = NULL; + const INT32 lastTargID = player->lastjawztarget; + mobj_t *lastTarg = NULL; + + INT32 targID = MAXPLAYERS; + mobj_t *targ = NULL; + mobj_t *ret = NULL; - if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS) + if (specialStage.active == true && lastTargID == MAXPLAYERS) + { + // Aiming at the UFO. + lastTarg = specialStage.ufo; + } + else if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS) && playeringame[lastTargID] == true) { if (players[lastTargID].spectator == false) { - lastTarg = &players[lastTargID]; + lastTarg = players[lastTargID].mo; } } if (player->throwdir == -1) { // Backwards Jawz targets yourself. - targ = player; + targ = player->mo; player->jawztargetdelay = 0; } else @@ -7970,9 +7984,14 @@ void K_KartPlayerAfterThink(player_t *player) targ = K_FindJawzTarget(player->mo, player, ANGLE_45); } - if (targ != NULL && targ->mo != NULL && P_MobjWasRemoved(targ->mo) == false) + if (targ != NULL && P_MobjWasRemoved(targ) == false) { - if (targ - players == lastTargID) + if (targ->player != NULL) + { + targID = targ->player - players; + } + + if (targID == lastTargID) { // Increment delay. if (player->jawztargetdelay < 10) @@ -7991,33 +8010,33 @@ void K_KartPlayerAfterThink(player_t *player) else { // Allow a swap. - if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ)) + if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ->player)) { S_StartSound(NULL, sfx_s3k89); } else { - S_StartSound(targ->mo, sfx_s3k89); + S_StartSound(targ, sfx_s3k89); } - player->lastjawztarget = targ - players; + player->lastjawztarget = targID; player->jawztargetdelay = 5; } } } - if (targ == NULL || targ->mo == NULL || P_MobjWasRemoved(targ->mo) == true) + if (targ == NULL || P_MobjWasRemoved(targ) == true) { player->lastjawztarget = -1; player->jawztargetdelay = 0; return; } - ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE); - ret->old_x = targ->mo->old_x; - ret->old_y = targ->mo->old_y; - ret->old_z = targ->mo->old_z; - P_SetTarget(&ret->target, targ->mo); + ret = P_SpawnMobj(targ->x, targ->y, targ->z, MT_PLAYERRETICULE); + ret->old_x = targ->old_x; + ret->old_y = targ->old_y; + ret->old_z = targ->old_z; + P_SetTarget(&ret->target, targ); ret->frame |= ((leveltime % 10) / 2); ret->tics = 1; ret->color = player->skincolor; diff --git a/src/k_kart.h b/src/k_kart.h index 8831fe0a4..4e891364d 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -118,7 +118,7 @@ void K_UpdateHnextList(player_t *player, boolean clean); void K_DropHnextList(player_t *player, boolean keepshields); void K_RepairOrbitChain(mobj_t *orbit); void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player); -player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range); +mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range); INT32 K_GetKartRingPower(player_t *player, boolean boosted); void K_UpdateDistanceFromFinishLine(player_t *const player); boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y); diff --git a/src/k_objects.h b/src/k_objects.h index d7abad6e5..ab7749865 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -59,6 +59,7 @@ mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration); /* Special Stage UFO */ void Obj_SpecialUFOThinker(mobj_t *bomb); +boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType); mobj_t *Obj_CreateSpecialUFO(void); UINT32 K_GetSpecialUFODistance(void); diff --git a/src/objects/jawz.c b/src/objects/jawz.c index cc241ba87..52a52a95c 100644 --- a/src/objects/jawz.c +++ b/src/objects/jawz.c @@ -140,17 +140,21 @@ static void JawzChase(mobj_t *th, boolean grounded) if (jawz_chase(th) == NULL || P_MobjWasRemoved(jawz_chase(th)) == true) { + mobj_t *newChase = NULL; + player_t *owner = NULL; + th->angle = K_MomentumAngle(th); - if (jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false - && jawz_owner(th)->player != NULL) + if ((jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false) + && (jawz_owner(th)->player != NULL)) { - player_t *newPlayer = K_FindJawzTarget(th, jawz_owner(th)->player, ANGLE_90); + owner = jawz_owner(th)->player; + } - if (newPlayer != NULL) - { - P_SetTarget(&jawz_chase(th), newPlayer->mo); - } + newChase = K_FindJawzTarget(th, owner, ANGLE_90); + if (newChase != NULL) + { + P_SetTarget(&jawz_chase(th), newChase); } } diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 3d2ad68d7..fa26ef51d 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -7,8 +7,8 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file shrink.c -/// \brief Shrink laser item code. +/// \file ufo.c +/// \brief Special Stage UFO #include "../doomdef.h" #include "../doomstat.h" @@ -24,11 +24,11 @@ #include "../k_waypoint.h" #include "../k_specialstage.h" -#define UFO_BASE_SPEED (12 * FRACUNIT) // UFO's slowest speed. -#define UFO_SPEEDUP (FRACUNIT) +#define UFO_BASE_SPEED (16 * FRACUNIT) // UFO's slowest speed. +#define UFO_SPEEDUP (FRACUNIT >> 3) #define UFO_SLOWDOWN (FRACUNIT >> 2) #define UFO_SPACING (1024 * FRACUNIT) -#define UFO_DEADZONE (512 * FRACUNIT) +#define UFO_DEADZONE (768 * FRACUNIT) #define UFO_SPEEDFACTOR (FRACUNIT * 9 / 10) #define ufo_waypoint(o) ((o)->extravalue1) @@ -49,6 +49,11 @@ static fixed_t GenericDistance( return P_AproxDistance(P_AproxDistance(destx - curx, desty - cury), destz - curz); } +static boolean UFOEmeraldChase(mobj_t *ufo) +{ + return (ufo->health <= 1); +} + static void UFOUpdateDistanceToFinish(mobj_t *ufo) { waypoint_t *finishLine = K_GetFinishLineWaypoint(); @@ -314,17 +319,86 @@ static void UFOMove(mobj_t *ufo) } } +static void UFOEmeraldVFX(mobj_t *ufo) +{ + if (leveltime % 3 == 0) + { + mobj_t *sparkle = P_SpawnMobjFromMobj( + ufo, + P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT, + P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT, + P_RandomRange(PR_SPARKLE, 0, 64) * FRACUNIT, + MT_EMERALDSPARK + ); + + sparkle->color = ufo->color; + sparkle->momz += 8 * ufo->scale * P_MobjFlip(ufo); + } +} + void Obj_SpecialUFOThinker(mobj_t *ufo) { UFOMove(ufo); UFOUpdateAngle(ufo); UFOUpdateDistanceToFinish(ufo); UFOUpdateSpeed(ufo); + + if (UFOEmeraldChase(ufo) == true) + { + // Spawn emerald sparkles + UFOEmeraldVFX(ufo); + } +} + +static UINT8 GetUFODamage(mobj_t *inflictor) +{ + if (inflictor == NULL || P_MobjWasRemoved(inflictor) == true) + { + return 1; + } + + switch (inflictor->type) + { + default: + { + return 1; + } + } +} + +boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType) +{ + UINT8 damage = 1; + + (void)source; + (void)damageType; + + if (UFOEmeraldChase(ufo) == true) + { + // Damaged fully already, no need for any more. + ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); // Double check flags, just to be sure. + return false; + } + + damage = GetUFODamage(inflictor); + + if (damage >= ufo->health - 1) + { + // Turn into just an emerald, and make it collectible! + ufo->health = 1; + ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); + return true; + } + + ufo->health -= damage; + K_SetHitLagForObjects(ufo, inflictor, damage * 6, true); + return true; } static mobj_t *InitSpecialUFO(waypoint_t *start) { mobj_t *ufo = NULL; + mobj_t *underlay = NULL; if (start == NULL) { @@ -343,6 +417,13 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) ufo_speed(ufo) = UFO_BASE_SPEED; + ufo->color = SKINCOLOR_CHAOSEMERALD1; + + underlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&underlay->target, ufo); + P_SetMobjState(underlay, S_CHAOSEMERALD_UNDER); + underlay->color = ufo->color; + return ufo; } diff --git a/src/p_inter.c b/src/p_inter.c index 6400b9d35..c1685bd70 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2222,6 +2222,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } else { + if (target->type == MT_SPECIAL_UFO) + { + return Obj_SpecialUFODamage(target, inflictor, source, damagetype); + } + if (damagetype & DMG_STEAL) { // Not a player, steal damage is intended to not do anything From e7ae65f30efd4ebba35eabed316a0777551eacf1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 22:11:18 -0500 Subject: [PATCH 15/40] Next round of UFO tweaks - Damage depends on item thrown at it. - UFO speeds up when getting damaged. - Made UFO speed up more often. - First Special Stage item column is active farther back. - Items can't be disabled if rules can't be changed. - Item column code with items disabled uses fractional precision. - Fixed bug with the new item table code to support different lengths better. - Tweaked UFO speed values again. - UFO is no longer solid. --- src/info.c | 2 +- src/k_kart.h | 1 + src/k_roulette.c | 9 ++++++-- src/objects/ufo.c | 57 ++++++++++++++++++++++++++++++++--------------- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/info.c b/src/info.c index a106464d7..ca4b802ba 100644 --- a/src/info.c +++ b/src/info.c @@ -29081,7 +29081,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 16, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SHOOTABLE|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, }; diff --git a/src/k_kart.h b/src/k_kart.h index 4e891364d..fcab35a44 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -56,6 +56,7 @@ UINT8 K_ItemResultToAmount(SINT8 getitem); tic_t K_GetItemCooldown(SINT8 itemResult); void K_SetItemCooldown(SINT8 itemResult, tic_t time); void K_RunItemCooldowns(void); +boolean K_ItemEnabled(SINT8 item); fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against); boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2); boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj); diff --git a/src/k_roulette.c b/src/k_roulette.c index 836aa29ba..ace2d6f5d 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -693,7 +693,12 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett { UINT8 j; - if (gametype == GT_BATTLE && i > 1) + if (specialStage.active == true && i > 3) + { + oddsvalid[i] = false; + continue; + } + else if (gametype == GT_BATTLE && i > 1) { oddsValid[i] = false; continue; @@ -724,7 +729,7 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett { if (specialStage.active == true) // Special Stages { - SETUPDISTTABLE(0,1); + SETUPDISTTABLE(0,2); SETUPDISTTABLE(1,2); SETUPDISTTABLE(2,3); SETUPDISTTABLE(3,1); diff --git a/src/objects/ufo.c b/src/objects/ufo.c index fa26ef51d..eba92c525 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -24,12 +24,12 @@ #include "../k_waypoint.h" #include "../k_specialstage.h" -#define UFO_BASE_SPEED (16 * FRACUNIT) // UFO's slowest speed. -#define UFO_SPEEDUP (FRACUNIT >> 3) -#define UFO_SLOWDOWN (FRACUNIT >> 2) -#define UFO_SPACING (1024 * FRACUNIT) -#define UFO_DEADZONE (768 * FRACUNIT) -#define UFO_SPEEDFACTOR (FRACUNIT * 9 / 10) +#define UFO_BASE_SPEED (24 * FRACUNIT) // UFO's slowest speed. +#define UFO_SPEEDUP (FRACUNIT >> 1) // Acceleration +#define UFO_SLOWDOWN (FRACUNIT >> 1) // Deceleration +#define UFO_SPACING (1024 * FRACUNIT) // How far the UFO wants to stay in front +#define UFO_DEADZONE (2048 * FRACUNIT) // Deadzone where it won't update it's speed as much. +#define UFO_SPEEDFACTOR (FRACUNIT * 9 / 10) // Factor of player's best speed, to make it more fair. #define ufo_waypoint(o) ((o)->extravalue1) #define ufo_distancetofinish(o) ((o)->extravalue2) @@ -157,17 +157,17 @@ static void UFOUpdateSpeed(mobj_t *ufo) distDelta = ufo_distancetofinish(ufo) - wantedDist; - if (abs(distDelta) <= deadzone) + if (distDelta > 0) { - // We're in a good spot, try to match the player. - wantedSpeed = max(bestSpeed >> 1, baseSpeed); + // Too far behind! Start speeding up! + wantedSpeed = max(bestSpeed << 1, baseSpeed << 2); } else { - if (distDelta > 0) + if (abs(distDelta) < deadzone) { - // Too far behind! Start speeding up! - wantedSpeed = max(bestSpeed << 1, baseSpeed << 2); + // We're in a good spot, try to match the player. + wantedSpeed = max(bestSpeed >> 1, baseSpeed); } else { @@ -354,14 +354,29 @@ static UINT8 GetUFODamage(mobj_t *inflictor) { if (inflictor == NULL || P_MobjWasRemoved(inflictor) == true) { - return 1; + // Default damage value. + return 10; } switch (inflictor->type) { + case MT_SPB: + { + // SPB deals triple damage. + return 30; + } + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + { + // Orbinauts deal double damage. + return 20; + } + case MT_JAWZ: + case MT_JAWZ_SHIELD: default: { - return 1; + // Jawz deal minimal damage. + return 10; } } } @@ -373,6 +388,9 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN (void)source; (void)damageType; + // Speed up on damage! + ufo_speed(ufo) += FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed)); + if (UFOEmeraldChase(ufo) == true) { // Damaged fully already, no need for any more. @@ -384,14 +402,14 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN if (damage >= ufo->health - 1) { - // Turn into just an emerald, and make it collectible! + // Destroy the UFO parts, and make the emerald collectible! ufo->health = 1; ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); return true; } ufo->health -= damage; - K_SetHitLagForObjects(ufo, inflictor, damage * 6, true); + K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true); return true; } @@ -415,15 +433,18 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) UFOUpdateDistanceToFinish(ufo); } - ufo_speed(ufo) = UFO_BASE_SPEED; + ufo_speed(ufo) = FixedMul(UFO_BASE_SPEED << 2, K_GetKartGameSpeedScalar(gamespeed)); + // TODO: Adjustable Special Stage emerald color ufo->color = SKINCOLOR_CHAOSEMERALD1; underlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); P_SetTarget(&underlay->target, ufo); - P_SetMobjState(underlay, S_CHAOSEMERALD_UNDER); underlay->color = ufo->color; + // TODO: Super Emeralds / Chaos Rings + P_SetMobjState(underlay, S_CHAOSEMERALD_UNDER); + return ufo; } From 577f505342192e7c44ca9f6c2fb5478ce4e523c2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 22:46:45 -0500 Subject: [PATCH 16/40] Set eventmode when testing Special Stages from ZB --- src/d_main.c | 12 ++++++++++++ src/p_mobj.c | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index fe5046585..bd737b530 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1863,6 +1863,18 @@ void D_SRB2Main(void) G_SetUsedCheats(); } + if (grandprixinfo.gp == true && mapheaderinfo[pstartmap-1]) + { + if (mapheaderinfo[pstartmap-1]->typeoflevel & TOL_SPECIAL) + { + specialStage.active = true; + specialStage.encore = grandprixinfo.encore; + grandprixinfo.eventmode = GPEVENT_SPECIAL; + } + + G_SetUsedCheats(); + } + D_MapChange(pstartmap, gametype, (cv_kartencore.value == 1), true, 0, false, false); } } diff --git a/src/p_mobj.c b/src/p_mobj.c index 7aa9a6fb4..0e6ad5647 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -46,6 +46,7 @@ #include "k_terrain.h" #include "k_collide.h" #include "k_objects.h" +#include "k_grandprix.h" static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); @@ -11468,13 +11469,12 @@ void P_SpawnPlayer(INT32 playernum) } else if (p->bot) { - /* - if (bonusgame || specialstage || boss) + if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE) { - // Bots should avoid + // Bots aren't supposed to be here. p->spectator = true; } - */ + else { // No point in a spectating bot! p->spectator = false; From 2ea84c290168bce5e62ec6b2f7d138b6e3caa00b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 22:54:57 -0500 Subject: [PATCH 17/40] Add prints for winning / losing for now. --- src/objects/ufo.c | 24 +++++++++++++++++++----- src/p_inter.c | 6 ++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index eba92c525..f3a1fb803 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -227,7 +227,13 @@ static void UFOMove(mobj_t *ufo) path_t pathtofinish = {0}; size_t pathIndex = 0; - curWaypoint = K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo)); + boolean reachedEnd = false; + + if (ufo_waypoint(ufo) >= 0) + { + curWaypoint = K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo)); + } + destWaypoint = K_GetFinishLineWaypoint(); if (curWaypoint == NULL || destWaypoint == NULL) @@ -275,6 +281,7 @@ static void UFOMove(mobj_t *ufo) if (curWaypoint == destWaypoint) { // Reached the end. + reachedEnd = true; break; } @@ -292,7 +299,7 @@ static void UFOMove(mobj_t *ufo) if (pathfindsuccess == false) { // Path isn't valid. - // Just transition into the next state. + // Just keep going. break; } } @@ -302,6 +309,7 @@ static void UFOMove(mobj_t *ufo) if (pathIndex >= pathtofinish.numnodes) { // Successfully reached the end of the path. + reachedEnd = true; break; } @@ -313,6 +321,12 @@ static void UFOMove(mobj_t *ufo) UFOMoveTo(ufo, newX, newY, newZ); + if (reachedEnd == true) + { + CONS_Printf("You lost...\n"); + ufo_waypoint(ufo) = -1; // Invalidate + } + if (pathfindsuccess == true) { Z_Free(pathtofinish.array); @@ -366,16 +380,16 @@ static UINT8 GetUFODamage(mobj_t *inflictor) return 30; } case MT_ORBINAUT: - case MT_ORBINAUT_SHIELD: { - // Orbinauts deal double damage. + // Thrown orbinauts deal double damage. return 20; } case MT_JAWZ: case MT_JAWZ_SHIELD: + case MT_ORBINAUT_SHIELD: default: { - // Jawz deal minimal damage. + // Jawz / shields deal regular damage. return 10; } } diff --git a/src/p_inter.c b/src/p_inter.c index c1685bd70..7daaff299 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -375,6 +375,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) player->emeralds |= special->extravalue1; K_CheckEmeralds(player); break; + case MT_SPECIAL_UFO: + if (!P_CanPickupItem(player, 0)) + return; + + CONS_Printf("You win!\n"); + break; /* case MT_EERIEFOG: special->frame &= ~FF_TRANS80; From eab34651bf9f43799ce7d56ba5713235afbf4f16 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 23:33:40 -0500 Subject: [PATCH 18/40] Sneakers deal contact damage to the UFO --- src/k_kart.c | 4 ++++ src/k_objects.h | 1 + src/objects/ufo.c | 47 +++++++++++++++++++++++++++++++++++++++++------ src/p_inter.c | 6 ++++++ src/p_map.c | 8 ++++++++ 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 86d177f90..429ce9046 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -529,6 +529,10 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against) { weight = 0; // This player does not cause any bump action } + else if (against && against->type == MT_SPECIAL_UFO) + { + weight = 0; + } else { // Applies rubberbanding, to prevent rubberbanding bots diff --git a/src/k_objects.h b/src/k_objects.h index ab7749865..0f2318ee8 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -60,6 +60,7 @@ mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration); /* Special Stage UFO */ void Obj_SpecialUFOThinker(mobj_t *bomb); boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType); +void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other); mobj_t *Obj_CreateSpecialUFO(void); UINT32 K_GetSpecialUFODistance(void); diff --git a/src/objects/ufo.c b/src/objects/ufo.c index f3a1fb803..852eb4bba 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -27,13 +27,14 @@ #define UFO_BASE_SPEED (24 * FRACUNIT) // UFO's slowest speed. #define UFO_SPEEDUP (FRACUNIT >> 1) // Acceleration #define UFO_SLOWDOWN (FRACUNIT >> 1) // Deceleration -#define UFO_SPACING (1024 * FRACUNIT) // How far the UFO wants to stay in front +#define UFO_SPACING (768 * FRACUNIT) // How far the UFO wants to stay in front #define UFO_DEADZONE (2048 * FRACUNIT) // Deadzone where it won't update it's speed as much. -#define UFO_SPEEDFACTOR (FRACUNIT * 9 / 10) // Factor of player's best speed, to make it more fair. +#define UFO_SPEEDFACTOR (FRACUNIT * 3 / 4) // Factor of player's best speed, to make it more fair. #define ufo_waypoint(o) ((o)->extravalue1) #define ufo_distancetofinish(o) ((o)->extravalue2) #define ufo_speed(o) ((o)->watertop) +#define ufo_collectdelay(o) ((o)->threshold) static void UFOMoveTo(mobj_t *ufo, fixed_t destx, fixed_t desty, fixed_t destz) { @@ -131,7 +132,7 @@ static void UFOUpdateSpeed(mobj_t *ufo) // 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 = min(bestSpeed, K_GetKartSpeed(player, false, false)); // Don't become unfair with Sneakers. bestSpeed = FixedDiv(bestSpeed, mapobjectscale); // Unscale from mapobjectscale to FRACUNIT bestSpeed = FixedMul(bestSpeed, UFO_SPEEDFACTOR); // Make it a bit more lenient } @@ -160,11 +161,11 @@ static void UFOUpdateSpeed(mobj_t *ufo) if (distDelta > 0) { // Too far behind! Start speeding up! - wantedSpeed = max(bestSpeed << 1, baseSpeed << 2); + wantedSpeed = max(bestSpeed, baseSpeed << 2); } else { - if (abs(distDelta) < deadzone) + if (abs(distDelta) <= deadzone) { // We're in a good spot, try to match the player. wantedSpeed = max(bestSpeed >> 1, baseSpeed); @@ -361,6 +362,11 @@ void Obj_SpecialUFOThinker(mobj_t *ufo) { // Spawn emerald sparkles UFOEmeraldVFX(ufo); + ufo_collectdelay(ufo)--; + } + else + { + ufo_collectdelay(ufo) = TICRATE; } } @@ -379,6 +385,11 @@ static UINT8 GetUFODamage(mobj_t *inflictor) // SPB deals triple damage. return 30; } + case MT_PLAYER: + { + // Players deal damage relative to how many sneakers they used. + return 15 * inflictor->player->numsneakers; + } case MT_ORBINAUT: { // Thrown orbinauts deal double damage. @@ -414,6 +425,13 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN damage = GetUFODamage(inflictor); + if (damage <= 0) + { + return false; + } + + K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true); + if (damage >= ufo->health - 1) { // Destroy the UFO parts, and make the emerald collectible! @@ -423,10 +441,27 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN } ufo->health -= damage; - K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true); return true; } +void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other) +{ + if (other->player == NULL) + { + return; + } + + if ((other->player->sneakertimer > 0) + && !P_PlayerInPain(other->player) + && (other->player->flashing == 0)) + { + // Bump and deal damage. + Obj_SpecialUFODamage(ufo, other, other, DMG_STEAL); + K_KartBouncing(other, ufo); + other->player->sneakertimer = 0; + } +} + static mobj_t *InitSpecialUFO(waypoint_t *start) { mobj_t *ufo = NULL; diff --git a/src/p_inter.c b/src/p_inter.c index 7daaff299..f96554c25 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -379,6 +379,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!P_CanPickupItem(player, 0)) return; + if (special->threshold > 0) + return; + + if (toucher->hitlag > 0) + return; + CONS_Printf("You win!\n"); break; /* diff --git a/src/p_map.c b/src/p_map.c index c7047bcde..9816643fe 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1348,6 +1348,14 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) return BMIT_CONTINUE; } + else if (thing->type == MT_SPECIAL_UFO) + { + if (!(thing->flags & MF_SPECIAL)) + { + Obj_PlayerUFOCollide(thing, tmthing); + return BMIT_CONTINUE; + } + } else if (thing->type == MT_BLUEROBRA_HEAD || thing->type == MT_BLUEROBRA_JOINT) { // see if it went over / under From 2c5df772c822a8c97ee3ac8c55b8ca20264e0115 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Nov 2022 23:35:20 -0500 Subject: [PATCH 19/40] Prevent possible overflow, just in case --- src/objects/ufo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 852eb4bba..7cb15ff27 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -506,7 +506,7 @@ mobj_t *Obj_CreateSpecialUFO(void) { const boolean huntbackwards = true; const boolean useshortcuts = false; - const UINT32 traveldist = UINT32_MAX; // Go as far back as possible. + const UINT32 traveldist = INT32_MAX; // Go as far back as possible. Not UINT32_MAX to avoid possible overflow. boolean pathfindsuccess = false; path_t pathtofinish = {0}; From a524422071bf07fc786a2d25a66b80b333e32077 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 27 Nov 2022 08:16:18 -0500 Subject: [PATCH 20/40] First pass on UFO visuals --- src/deh_tables.c | 6 ++ src/info.c | 40 +++++++- src/info.h | 10 ++ src/k_objects.h | 5 +- src/objects/ufo.c | 236 +++++++++++++++++++++++++++++++++++++++++++--- src/p_map.c | 2 +- src/p_mobj.c | 31 +++++- 7 files changed, 313 insertions(+), 17 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index b60bf269e..d856a33c3 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4534,6 +4534,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi // Broly Ki Orb "S_BROLY1", "S_BROLY2", + + "S_SPECIAL_UFO_POD", + "S_SPECIAL_UFO_OVERLAY", + "S_SPECIAL_UFO_ARM", + "S_SPECIAL_UFO_STEM", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -5631,6 +5636,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BROLY", "MT_SPECIAL_UFO", + "MT_SPECIAL_UFO_PIECE", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index ca4b802ba..84bfc7c17 100644 --- a/src/info.c +++ b/src/info.c @@ -785,6 +785,10 @@ char sprnames[NUMSPRITES + 1][5] = "FLBM", + "UFOB", + "UFOA", + "UFOS", + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later "VIEW", }; @@ -5146,6 +5150,11 @@ state_t states[NUMSTATES] = // Broly Ki Orb {SPR_LSSJ, FF_REVERSESUBTRACT|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_BROLY2}, // S_BROLY1 {SPR_NULL, 0, 5*TICRATE, {A_SSMineFlash}, 0, 0, S_NULL}, // S_BROLY2 + + {SPR_UFOB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_POD + {SPR_UFOB, 1|FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 1, 1, S_NULL}, // S_SPECIAL_UFO_OVERLAY + {SPR_UFOA, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_ARM + {SPR_UFOS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_STEM }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -29058,7 +29067,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_SPECIAL_UFO + { // MT_SPECIAL_UFO -1, // doomednum S_CHAOSEMERALD1, // spawnstate 101, // spawnhealth @@ -29075,7 +29084,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 72*FRACUNIT, // radius + 108*FRACUNIT, // radius 72*FRACUNIT, // height 0, // display offset 16, // mass @@ -29084,6 +29093,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_SHOOTABLE|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, + + { // MT_SPECIAL_UFO_PIECE + -1, // doomednum + S_INVISIBLE, // 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 + 8*FRACUNIT, // radius + 16*FRACUNIT, // height + 1, // display offset + 100, // mass + 1, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, }; skincolor_t skincolors[MAXSKINCOLORS] = { diff --git a/src/info.h b/src/info.h index fac8100c6..cf2b5d6bd 100644 --- a/src/info.h +++ b/src/info.h @@ -1332,6 +1332,10 @@ typedef enum sprite SPR_FLBM, // Finish line beam + SPR_UFOB, + SPR_UFOA, + SPR_UFOS, + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later SPR_VIEW, @@ -5569,6 +5573,11 @@ typedef enum state S_BROLY1, S_BROLY2, + S_SPECIAL_UFO_POD, + S_SPECIAL_UFO_OVERLAY, + S_SPECIAL_UFO_ARM, + S_SPECIAL_UFO_STEM, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -6685,6 +6694,7 @@ typedef enum mobj_type MT_BROLY, MT_SPECIAL_UFO, + MT_SPECIAL_UFO_PIECE, MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, diff --git a/src/k_objects.h b/src/k_objects.h index 0f2318ee8..d9d89321e 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -58,9 +58,12 @@ void Obj_DuelBombInit(mobj_t *bomb); mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration); /* Special Stage UFO */ -void Obj_SpecialUFOThinker(mobj_t *bomb); +void Obj_SpecialUFOThinker(mobj_t *ufo); boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType); void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other); +void Obj_UFOPieceThink(mobj_t *piece); +void Obj_UFOPieceDead(mobj_t *piece); +void Obj_UFOPieceRemoved(mobj_t *piece); mobj_t *Obj_CreateSpecialUFO(void); UINT32 K_GetSpecialUFODistance(void); diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 7cb15ff27..9672d840e 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -8,7 +8,7 @@ // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file ufo.c -/// \brief Special Stage UFO +/// \brief Special Stage UFO + Emerald handler #include "../doomdef.h" #include "../doomstat.h" @@ -31,11 +31,29 @@ #define UFO_DEADZONE (2048 * FRACUNIT) // Deadzone where it won't update it's speed as much. #define UFO_SPEEDFACTOR (FRACUNIT * 3 / 4) // Factor of player's best speed, to make it more fair. +#define UFO_NUMARMS (3) +#define UFO_ARMDELTA (ANGLE_MAX / UFO_NUMARMS) + #define ufo_waypoint(o) ((o)->extravalue1) #define ufo_distancetofinish(o) ((o)->extravalue2) #define ufo_speed(o) ((o)->watertop) #define ufo_collectdelay(o) ((o)->threshold) +#define ufo_pieces(o) ((o)->hnext) + +#define ufo_piece_type(o) ((o)->extravalue1) + +#define ufo_piece_owner(o) ((o)->target) +#define ufo_piece_next(o) ((o)->hnext) +#define ufo_piece_prev(o) ((o)->hprev) + +enum +{ + UFO_PIECE_TYPE_POD, + UFO_PIECE_TYPE_ARM, + UFO_PIECE_TYPE_STEM, +}; + static void UFOMoveTo(mobj_t *ufo, fixed_t destx, fixed_t desty, fixed_t destz) { ufo->momx = destx - ufo->x; @@ -55,6 +73,11 @@ static boolean UFOEmeraldChase(mobj_t *ufo) return (ufo->health <= 1); } +static boolean UFOPieceValid(mobj_t *piece) +{ + return (piece != NULL && P_MobjWasRemoved(piece) == false && piece->health > 0); +} + static void UFOUpdateDistanceToFinish(mobj_t *ufo) { waypoint_t *finishLine = K_GetFinishLineWaypoint(); @@ -221,6 +244,7 @@ static void UFOMove(mobj_t *ufo) fixed_t newX = ufo->x; fixed_t newY = ufo->y; fixed_t newZ = ufo->z; + const fixed_t floatHeight = 24 * ufo->scale; const boolean useshortcuts = false; const boolean huntbackwards = false; @@ -253,7 +277,7 @@ static void UFOMove(mobj_t *ufo) { fixed_t wpX = curWaypoint->mobj->x; fixed_t wpY = curWaypoint->mobj->y; - fixed_t wpZ = curWaypoint->mobj->z; + fixed_t wpZ = curWaypoint->mobj->z + floatHeight; fixed_t distToNext = GenericDistance( newX, newY, newZ, @@ -336,13 +360,19 @@ static void UFOMove(mobj_t *ufo) static void UFOEmeraldVFX(mobj_t *ufo) { + const INT32 bobS = 32; + const angle_t bobA = (leveltime & (bobS - 1)) * (ANGLE_MAX / bobS); + const fixed_t bobH = 16 * ufo->scale; + + ufo->sprzoff = FixedMul(bobH, FINESINE(bobA >> ANGLETOFINESHIFT)); + if (leveltime % 3 == 0) { mobj_t *sparkle = P_SpawnMobjFromMobj( ufo, P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT, P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT, - P_RandomRange(PR_SPARKLE, 0, 64) * FRACUNIT, + (P_RandomRange(PR_SPARKLE, 0, 64) * FRACUNIT) + FixedDiv(ufo->sprzoff, ufo->scale), MT_EMERALDSPARK ); @@ -370,6 +400,65 @@ void Obj_SpecialUFOThinker(mobj_t *ufo) } } +static void UFOCopyHitlagToPieces(mobj_t *ufo) +{ + mobj_t *piece = NULL; + + piece = ufo_pieces(ufo); + while (UFOPieceValid(piece) == true) + { + piece->hitlag = ufo->hitlag; + piece->eflags = (piece->eflags & ~MFE_DAMAGEHITLAG) | (ufo->eflags & MFE_DAMAGEHITLAG); + piece = ufo_piece_next(piece); + } +} + +static void UFOKillPiece(mobj_t *piece) +{ + angle_t dir = ANGLE_MAX; + fixed_t thrust = 0; + + if (UFOPieceValid(piece) == false) + { + return; + } + + piece->health = 0; + piece->tics = TICRATE; + piece->flags &= ~MF_NOGRAVITY; + + switch (ufo_piece_type(piece)) + { + case UFO_PIECE_TYPE_ARM: + { + dir = piece->angle; + thrust = 12 * piece->scale; + break; + } + default: + { + dir = FixedAngle(P_RandomRange(PR_DECORATION, 0, 359) << FRACBITS); + thrust = 4 * piece->scale; + break; + } + } + + P_Thrust(piece, dir, -thrust); + P_SetObjectMomZ(piece, 12*FRACUNIT, true); +} + +static void UFOKillPieces(mobj_t *ufo) +{ + mobj_t *piece = NULL; + + piece = ufo_pieces(ufo); + while (UFOPieceValid(piece) == true) + { + UFOKillPiece(piece); + piece = ufo_piece_next(piece); + } +} + static UINT8 GetUFODamage(mobj_t *inflictor) { if (inflictor == NULL || P_MobjWasRemoved(inflictor) == true) @@ -408,18 +497,15 @@ static UINT8 GetUFODamage(mobj_t *inflictor) boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType) { + const fixed_t addSpeed = FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed)); UINT8 damage = 1; (void)source; (void)damageType; - // Speed up on damage! - ufo_speed(ufo) += FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed)); - if (UFOEmeraldChase(ufo) == true) { // Damaged fully already, no need for any more. - ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); // Double check flags, just to be sure. return false; } @@ -430,13 +516,22 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN return false; } + // Speed up on damage! + ufo_speed(ufo) += addSpeed; + K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true); + UFOCopyHitlagToPieces(ufo); if (damage >= ufo->health - 1) { // Destroy the UFO parts, and make the emerald collectible! + UFOKillPieces(ufo); + ufo->health = 1; ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); + ufo->shadowscale = FRACUNIT/3; + + ufo_speed(ufo) += addSpeed; // Even more speed! return true; } @@ -462,10 +557,96 @@ void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other) } } +void Obj_UFOPieceThink(mobj_t *piece) +{ + mobj_t *ufo = ufo_piece_owner(piece); + + if (ufo == NULL || P_MobjWasRemoved(ufo) == true) + { + P_KillMobj(piece, NULL, NULL, DMG_NORMAL); + return; + } + + piece->destscale = ufo->destscale; + piece->scalespeed = ufo->scalespeed; + + switch (ufo_piece_type(piece)) + { + case UFO_PIECE_TYPE_POD: + { + UFOMoveTo(piece, ufo->x, ufo->y, ufo->z + (120 * ufo->scale)); + break; + } + case UFO_PIECE_TYPE_ARM: + { + fixed_t dis = (104 * ufo->scale); + + fixed_t x = ufo->x - FixedMul(dis, FINECOSINE(piece->angle >> ANGLETOFINESHIFT)); + fixed_t y = ufo->y - FixedMul(dis, FINESINE(piece->angle >> ANGLETOFINESHIFT)); + + UFOMoveTo(piece, x, y, ufo->z + (24 * ufo->scale)); + + piece->angle -= FixedMul(ANG2, FixedDiv(ufo_speed(ufo), UFO_BASE_SPEED)); + break; + } + default: + { + P_KillMobj(piece, NULL, NULL, DMG_NORMAL); + return; + } + } +} + +void Obj_UFOPieceDead(mobj_t *piece) +{ + piece->renderflags ^= RF_DONTDRAW; +} + +void Obj_UFOPieceRemoved(mobj_t *piece) +{ + // Repair piece list. + mobj_t *ufo = ufo_piece_owner(piece); + mobj_t *next = ufo_piece_next(piece); + mobj_t *prev = ufo_piece_prev(piece); + + if (prev != NULL && P_MobjWasRemoved(prev) == false) + { + P_SetTarget( + &ufo_piece_next(prev), + (next != NULL && P_MobjWasRemoved(next) == false) ? next : NULL + ); + } + + if (next != NULL && P_MobjWasRemoved(next) == false) + { + P_SetTarget( + &ufo_piece_prev(next), + (prev != NULL && P_MobjWasRemoved(prev) == false) ? prev : NULL + ); + + if (ufo != NULL && P_MobjWasRemoved(ufo) == false) + { + if (piece == ufo_pieces(ufo)) + { + P_SetTarget( + &ufo_pieces(ufo), + next + ); + } + } + } + + P_SetTarget(&ufo_piece_next(piece), NULL); + P_SetTarget(&ufo_piece_prev(piece), NULL); +} + static mobj_t *InitSpecialUFO(waypoint_t *start) { mobj_t *ufo = NULL; - mobj_t *underlay = NULL; + mobj_t *overlay = NULL; + mobj_t *piece = NULL; + mobj_t *prevPiece = NULL; + size_t i; if (start == NULL) { @@ -487,12 +668,43 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) // TODO: Adjustable Special Stage emerald color ufo->color = SKINCOLOR_CHAOSEMERALD1; - underlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); - P_SetTarget(&underlay->target, ufo); - underlay->color = ufo->color; + overlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&overlay->target, ufo); + overlay->color = ufo->color; // TODO: Super Emeralds / Chaos Rings - P_SetMobjState(underlay, S_CHAOSEMERALD_UNDER); + P_SetMobjState(overlay, S_CHAOSEMERALD_UNDER); + + // Create UFO pieces. + // First: UFO center. + piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); + P_SetTarget(&ufo_piece_owner(piece), ufo); + + P_SetMobjState(piece, S_SPECIAL_UFO_POD); + ufo_piece_type(piece) = UFO_PIECE_TYPE_POD; + + overlay = P_SpawnMobjFromMobj(piece, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&overlay->target, piece); + P_SetMobjState(overlay, S_SPECIAL_UFO_OVERLAY); + + P_SetTarget(&ufo_pieces(ufo), piece); + prevPiece = piece; + + for (i = 0; i < UFO_NUMARMS; i++) + { + piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); + P_SetTarget(&ufo_piece_owner(piece), ufo); + + P_SetMobjState(piece, S_SPECIAL_UFO_ARM); + ufo_piece_type(piece) = UFO_PIECE_TYPE_ARM; + + piece->angle = UFO_ARMDELTA * i; + + P_SetTarget(&ufo_piece_next(prevPiece), piece); + P_SetTarget(&ufo_piece_prev(piece), prevPiece); + + prevPiece = piece; + } return ufo; } diff --git a/src/p_map.c b/src/p_map.c index 9816643fe..ee18c2b81 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1352,7 +1352,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) { if (!(thing->flags & MF_SPECIAL)) { - Obj_PlayerUFOCollide(thing, tmthing); + Obj_PlayerUFOCollide(thing, tm.thing); return BMIT_CONTINUE; } } diff --git a/src/p_mobj.c b/src/p_mobj.c index 0e6ad5647..fcbfeafa7 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5298,10 +5298,22 @@ void P_RunOverlays(void) mo->pitch = mo->target->pitch; mo->roll = mo->target->roll; + mo->spritexoffset = mo->target->spritexoffset; + mo->spriteyoffset = mo->target->spriteyoffset; + mo->spritexscale = mo->target->spritexscale; + mo->spriteyscale = mo->target->spriteyscale; + + mo->sprxoff = mo->target->sprxoff; + mo->spryoff = mo->target->spryoff; + mo->sprzoff = mo->target->sprzoff; + + mo->hitlag = mo->target->hitlag; + mo->eflags = (mo->eflags & ~MFE_DAMAGEHITLAG) | (mo->target->eflags & MFE_DAMAGEHITLAG); + if ((mo->flags & MF_DONTENCOREMAP) != (mo->target->flags & MF_DONTENCOREMAP)) mo->flags ^= MF_DONTENCOREMAP; - mo->dispoffset = mo->target->dispoffset + mo->info->dispoffset; + mo->dispoffset = mo->target->dispoffset; if (!(mo->state->frame & FF_ANIMATE)) { @@ -5321,6 +5333,7 @@ void P_RunOverlays(void) // if you're using FF_ANIMATE on an overlay, // then you're on your own. zoffs = 0; + mo->dispoffset++; } P_UnsetThingPosition(mo); @@ -6743,6 +6756,11 @@ static boolean P_MobjDeadThink(mobj_t *mobj) S_StartSound(dust, sfx_s3k3d); } break; + case MT_SPECIAL_UFO_PIECE: + { + Obj_UFOPieceDead(mobj); + break; + } default: break; } @@ -7297,6 +7315,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_SpecialUFOThinker(mobj); break; } + case MT_SPECIAL_UFO_PIECE: + { + Obj_UFOPieceThink(mobj); + break; + } case MT_EMERALD: { if (battleovertime.enabled >= 10*TICRATE) @@ -10050,6 +10073,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) case MT_PLAYER: case MT_KART_LEFTOVER: case MT_BATTLECAPSULE: + case MT_SPECIAL_UFO: thing->shadowscale = FRACUNIT; break; case MT_SMALLMACE: @@ -10906,6 +10930,11 @@ void P_RemoveMobj(mobj_t *mobj) Obj_ShrinkGunRemoved(mobj); } + if (mobj->type == MT_SPECIAL_UFO_PIECE) + { + Obj_UFOPieceRemoved(mobj); + } + mobj->health = 0; // Just because // unlink from sector and block lists From 6b78cd2428f0953766f2c78868c68c3c5b19a8a6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 27 Nov 2022 19:09:30 -0500 Subject: [PATCH 21/40] Adjust UFO pieces --- src/objects/ufo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 9672d840e..99feb055e 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -567,24 +567,24 @@ void Obj_UFOPieceThink(mobj_t *piece) return; } - piece->destscale = ufo->destscale; + piece->destscale = 3 * ufo->destscale / 2; piece->scalespeed = ufo->scalespeed; switch (ufo_piece_type(piece)) { case UFO_PIECE_TYPE_POD: { - UFOMoveTo(piece, ufo->x, ufo->y, ufo->z + (120 * ufo->scale)); + UFOMoveTo(piece, ufo->x, ufo->y, ufo->z + (132 * piece->scale)); break; } case UFO_PIECE_TYPE_ARM: { - fixed_t dis = (104 * ufo->scale); + fixed_t dis = (88 * piece->scale); fixed_t x = ufo->x - FixedMul(dis, FINECOSINE(piece->angle >> ANGLETOFINESHIFT)); fixed_t y = ufo->y - FixedMul(dis, FINESINE(piece->angle >> ANGLETOFINESHIFT)); - UFOMoveTo(piece, x, y, ufo->z + (24 * ufo->scale)); + UFOMoveTo(piece, x, y, ufo->z + (24 * piece->scale)); piece->angle -= FixedMul(ANG2, FixedDiv(ufo_speed(ufo), UFO_BASE_SPEED)); break; From f51a913710d1e92884802acafd999f9edae70e53 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 27 Nov 2022 19:37:32 -0500 Subject: [PATCH 22/40] Add stem --- src/info.c | 2 +- src/objects/ufo.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/info.c b/src/info.c index 84bfc7c17..2c4213fce 100644 --- a/src/info.c +++ b/src/info.c @@ -29117,7 +29117,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_NOSQUISH, // flags S_NULL // raisestate }, }; diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 99feb055e..b9b8c2def 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -429,6 +429,11 @@ static void UFOKillPiece(mobj_t *piece) switch (ufo_piece_type(piece)) { + case UFO_PIECE_TYPE_STEM: + { + piece->tics = 1; + return; + } case UFO_PIECE_TYPE_ARM: { dir = piece->angle; @@ -589,9 +594,18 @@ void Obj_UFOPieceThink(mobj_t *piece) piece->angle -= FixedMul(ANG2, FixedDiv(ufo_speed(ufo), UFO_BASE_SPEED)); break; } + case UFO_PIECE_TYPE_STEM: + { + fixed_t stemZ = ufo->z + (294 * piece->scale); + fixed_t sc = FixedDiv(FixedDiv(ufo->ceilingz - stemZ, piece->scale), 15 * FRACUNIT); + + UFOMoveTo(piece, ufo->x, ufo->y, stemZ); + piece->spriteyscale = sc; + break; + } default: { - P_KillMobj(piece, NULL, NULL, DMG_NORMAL); + P_RemoveMobj(piece); return; } } @@ -690,6 +704,7 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) P_SetTarget(&ufo_pieces(ufo), piece); prevPiece = piece; + // Add the catcher arms. for (i = 0; i < UFO_NUMARMS; i++) { piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); @@ -702,10 +717,20 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) P_SetTarget(&ufo_piece_next(prevPiece), piece); P_SetTarget(&ufo_piece_prev(piece), prevPiece); - prevPiece = piece; } + // Add the stem. + piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); + P_SetTarget(&ufo_piece_owner(piece), ufo); + + P_SetMobjState(piece, S_SPECIAL_UFO_STEM); + ufo_piece_type(piece) = UFO_PIECE_TYPE_STEM; + + P_SetTarget(&ufo_piece_next(prevPiece), piece); + P_SetTarget(&ufo_piece_prev(piece), prevPiece); + prevPiece = piece; + return ufo; } From 36e9a56f2907bf43f6757da5a450a8eaa40fd245 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 17 Dec 2022 01:05:30 -0500 Subject: [PATCH 23/40] Add K_TimeAttackRules Lets Time Attack capsules spawn in Free Play, and prevents Time Attack specific rules from happening in Special Stages --- src/discord.c | 2 +- src/k_kart.c | 39 ++++++++++++++++++++++++++++++++++++++- src/k_kart.h | 4 +++- src/k_roulette.c | 14 +++++++------- src/p_inter.c | 4 ++-- src/p_mobj.c | 2 +- 6 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/discord.c b/src/discord.c index 0e9cb35c9..25ba4605b 100644 --- a/src/discord.c +++ b/src/discord.c @@ -547,7 +547,7 @@ void DRPC_UpdatePresence(void) if (gamestate == GS_LEVEL && Playing()) { const time_t currentTime = time(NULL); - const time_t mapTimeStart = currentTime - ((leveltime + (modeattacking ? starttime : 0)) / TICRATE); + const time_t mapTimeStart = currentTime - ((leveltime + starttime) / TICRATE); discordPresence.startTimestamp = mapTimeStart; diff --git a/src/k_kart.c b/src/k_kart.c index 429ce9046..d94ee10ac 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -506,6 +506,43 @@ void K_RunItemCooldowns(void) } } +boolean K_TimeAttackRules(void) +{ + UINT8 playing = 0; + UINT8 i; + + if (specialStage.active == true) + { + // Kind of a hack -- Special Stages + // are expected to be 1-player, so + // we won't use the Time Attack changes + return false; + } + + if (modeattacking != ATTACKING_NONE) + { + // Time Attack obviously uses Time Attack rules :p + return true; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false || players[i].spectator == true) + { + continue; + } + + playing++; + if (playing > 1) + { + break; + } + } + + // Use Time Attack gameplay rules with only 1P. + return (playing <= 1); +} + //} //{ SRB2kart p_user.c Stuff @@ -3208,7 +3245,7 @@ boolean K_PlayerShrinkCheat(player_t *player) return ( (player->pflags & PF_SHRINKACTIVE) && (player->bot == false) - && (modeattacking == false) // Anyone want to make another record attack category? + && (modeattacking == ATTACKING_NONE) // Anyone want to make another record attack category? ); } diff --git a/src/k_kart.h b/src/k_kart.h index fcab35a44..4db338643 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -56,7 +56,9 @@ UINT8 K_ItemResultToAmount(SINT8 getitem); tic_t K_GetItemCooldown(SINT8 itemResult); void K_SetItemCooldown(SINT8 itemResult, tic_t time); void K_RunItemCooldowns(void); -boolean K_ItemEnabled(SINT8 item); + +boolean K_TimeAttackRules(void); + fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against); boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2); boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj); diff --git a/src/k_roulette.c b/src/k_roulette.c index ace2d6f5d..4b1897de4 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -502,12 +502,12 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, else if (specialStage.active == true) { I_Assert(pos < 4); // Ditto - newodds = K_KartItemOddsSpecial[item-1][pos]; + newOdds = K_KartItemOddsSpecial[item-1][pos]; } else { I_Assert(pos < 8); // Ditto - newodds = K_KartItemOddsRace[item-1][pos]; + newOdds = K_KartItemOddsRace[item-1][pos]; } newOdds <<= FRACBITS; @@ -693,12 +693,12 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett { UINT8 j; - if (specialStage.active == true && i > 3) + if (gametype == GT_BATTLE && i > 1) { - oddsvalid[i] = false; + oddsValid[i] = false; continue; } - else if (gametype == GT_BATTLE && i > 1) + else if (specialStage.active == true && i > 3) { oddsValid[i] = false; continue; @@ -1016,7 +1016,7 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette) fixed_t progress = 0; fixed_t total = 0; - if (modeattacking || roulette->playing <= 1) + if (K_TimeAttackRules() == true) { // Time Attack rules; use a consistent speed. roulette->tics = roulette->speed = ROULETTE_SPEED_TIMEATTACK; @@ -1099,7 +1099,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet return; } - else if (modeattacking || roulette->playing <= 1) + else if (K_TimeAttackRules() == true) { switch (gametype) { diff --git a/src/p_inter.c b/src/p_inter.c index f96554c25..81fd5115c 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1367,7 +1367,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget INT16 spacing = (target->radius >> 1) / target->scale; // set respawn fuse - if (modeattacking) // no respawns + if (K_TimeAttackRules() == true) // no respawns ; else if (target->threshold == KITEM_SUPERRING) target->fuse = 20*TICRATE; @@ -2212,7 +2212,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_PlayPainSound(target, source); - if ((hardhit == true) || (cv_kartdebughuddrop.value && !modeattacking)) + if ((hardhit == true) || cv_kartdebughuddrop.value) { K_DropItems(player); } diff --git a/src/p_mobj.c b/src/p_mobj.c index fcbfeafa7..d4e2d46a3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11974,7 +11974,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) // in record attack, only spawn ring capsules // (behavior can be inverted with the Extra flag, i.e. item capsule spawns and ring capsule does not) - if (modeattacking + if (K_TimeAttackRules() == true && (!(mthing->args[2] & TMICF_INVERTTIMEATTACK) == !isRingCapsule)) return false; } From 9ba5a97aa445258b30137adcd88914e6cc50caf0 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 18 Dec 2022 01:00:28 -0500 Subject: [PATCH 24/40] Set actual firstDist for Special Stages Fixes wild roulette speed --- src/k_roulette.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index 4b1897de4..e0bc3c325 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -897,19 +897,36 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->exiting++; } - if (players[i].position == 1) + if (specialStage.active == true) { - roulette->firstDist = K_UndoMapScaling(players[i].distancetofinish); + UINT32 dis = K_UndoMapScaling(players[i].distancetofinish); + if (dis < roulette->secondDist) + { + roulette->secondDist = dis; + } } - - if (players[i].position == 2) + else { - roulette->secondDist = K_UndoMapScaling(players[i].distancetofinish); + if (players[i].position == 1) + { + roulette->firstDist = K_UndoMapScaling(players[i].distancetofinish); + } + + if (players[i].position == 2) + { + roulette->secondDist = K_UndoMapScaling(players[i].distancetofinish); + } } } + if (specialStage.active == true) + { + roulette->firstDist = K_UndoMapScaling(K_GetSpecialUFODistance()); + } + // Calculate 2nd's distance from 1st, for SPB - if (roulette->firstDist != UINT32_MAX && roulette->secondDist != UINT32_MAX) + if (roulette->firstDist != UINT32_MAX && roulette->secondDist != UINT32_MAX + && roulette->secondDist > roulette->firstDist) { roulette->secondToFirst = roulette->secondDist - roulette->firstDist; roulette->secondToFirst = K_ScaleItemDistance(roulette->secondToFirst, 16 - roulette->playing); // Reversed scaling From 526a2b7de191a86a34bd4a0537e88d4af5c8509d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 18 Dec 2022 02:43:31 -0500 Subject: [PATCH 25/40] Fix not being able to use map command to go to SS --- src/d_netcmd.c | 117 +++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9820eeed3..da7e87d1c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2881,7 +2881,7 @@ static void Command_Map_f(void) if (mapheaderinfo[newmapnum-1]) { // Let's just guess so we don't have to specify the gametype EVERY time... - newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & TOL_RACE) ? GT_RACE : GT_BATTLE; + newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & TOL_BATTLE) ? GT_BATTLE : GT_RACE; } } @@ -2930,11 +2930,59 @@ static void Command_Map_f(void) if (!(netgame || multiplayer)) { + grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value); + grandprixinfo.masterbots = false; + + if (option_skill) + { + const char *skillname = COM_Argv(option_skill + 1); + INT32 newskill = -1; + INT32 j; + + for (j = 0; gpdifficulty_cons_t[j].strvalue; j++) + { + if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, skillname)) + { + newskill = (INT16)gpdifficulty_cons_t[j].value; + break; + } + } + + if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match + { + INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too + if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER) + newskill = (INT16)num; + } + + if (newskill != -1) + { + if (newskill == KARTGP_MASTER) + { + grandprixinfo.gamespeed = KARTSPEED_HARD; + grandprixinfo.masterbots = true; + } + else + { + grandprixinfo.gamespeed = newskill; + grandprixinfo.masterbots = false; + } + } + } + + grandprixinfo.encore = newencoremode; + + grandprixinfo.gp = true; + grandprixinfo.roundnum = 0; + grandprixinfo.cup = NULL; + grandprixinfo.wonround = false; + grandprixinfo.initalize = true; + + grandprixinfo.eventmode = GPEVENT_NONE; + if (newgametype == GT_BATTLE) { - grandprixinfo.gp = false; - specialStage.active = false; - K_ResetBossInfo(); + grandprixinfo.eventmode = GPEVENT_BONUS; if (mapheaderinfo[newmapnum-1] && mapheaderinfo[newmapnum-1]->typeoflevel & TOL_BOSS) @@ -2942,71 +2990,24 @@ static void Command_Map_f(void) bossinfo.boss = true; bossinfo.encore = newencoremode; } + else + { + bossinfo.boss = false; + K_ResetBossInfo(); + } } else { if (mapheaderinfo[newmapnum-1] && mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage { - grandprixinfo.gp = false; - bossinfo.boss = false; - specialStage.active = true; specialStage.encore = newencoremode; + grandprixinfo.eventmode = GPEVENT_SPECIAL; } - else // default GP + else { - grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value); - grandprixinfo.masterbots = false; - - if (option_skill) - { - const char *skillname = COM_Argv(option_skill + 1); - INT32 newskill = -1; - INT32 j; - - for (j = 0; gpdifficulty_cons_t[j].strvalue; j++) - { - if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, skillname)) - { - newskill = (INT16)gpdifficulty_cons_t[j].value; - break; - } - } - - if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match - { - INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too - if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER) - newskill = (INT16)num; - } - - if (newskill != -1) - { - if (newskill == KARTGP_MASTER) - { - grandprixinfo.gamespeed = KARTSPEED_HARD; - grandprixinfo.masterbots = true; - } - else - { - grandprixinfo.gamespeed = newskill; - grandprixinfo.masterbots = false; - } - } - } - - grandprixinfo.encore = newencoremode; - - grandprixinfo.gp = true; - grandprixinfo.roundnum = 0; - grandprixinfo.cup = NULL; - grandprixinfo.wonround = false; - - bossinfo.boss = false; specialStage.active = false; - - grandprixinfo.initalize = true; } } } From abcff39cd53321fb7d7283c413a395829e5098d3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 18 Dec 2022 02:50:59 -0500 Subject: [PATCH 26/40] Give UFO MF_NOCLIPHEIGHT Makes it go thru fences + not get caught on slopes --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index 2c4213fce..4612791b0 100644 --- a/src/info.c +++ b/src/info.c @@ -29090,7 +29090,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 16, // mass 0, // damage sfx_None, // activesound - MF_SHOOTABLE|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SHOOTABLE|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, From ad005f461e26b4e5beb4954c474ce7d0157131c5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 18 Dec 2022 03:30:57 -0500 Subject: [PATCH 27/40] Increase UFO base speed --- src/objects/ufo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index b9b8c2def..2706be543 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -24,7 +24,7 @@ #include "../k_waypoint.h" #include "../k_specialstage.h" -#define UFO_BASE_SPEED (24 * FRACUNIT) // UFO's slowest speed. +#define UFO_BASE_SPEED (42 * FRACUNIT) // UFO's slowest speed. #define UFO_SPEEDUP (FRACUNIT >> 1) // Acceleration #define UFO_SLOWDOWN (FRACUNIT >> 1) // Deceleration #define UFO_SPACING (768 * FRACUNIT) // How far the UFO wants to stay in front From 5f005cb9a7a366e578cbe62546e5178d74f029a4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 18 Dec 2022 03:31:47 -0500 Subject: [PATCH 28/40] Bananas deal 30 damage instead of default of 10 --- src/objects/ufo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 2706be543..5d609fbf1 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -475,6 +475,7 @@ static UINT8 GetUFODamage(mobj_t *inflictor) switch (inflictor->type) { case MT_SPB: + case MT_BANANA: { // SPB deals triple damage. return 30; From f9da9b06175677f1de3b89656d0fe4d0ac86d26d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 18 Dec 2022 04:44:01 -0500 Subject: [PATCH 29/40] Fix it still getting caught I'm scared of raw MF_NOCLIP but maybe it's OK --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index 4612791b0..23d2a31c9 100644 --- a/src/info.c +++ b/src/info.c @@ -29090,7 +29090,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 16, // mass 0, // damage sfx_None, // activesound - MF_SHOOTABLE|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + MF_SHOOTABLE|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, From f89027e95e2a70d2e65c10ffc31e577a7a70e2e4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 18 Dec 2022 04:45:20 -0500 Subject: [PATCH 30/40] Fix it speeding off too fast at the start now --- src/objects/ufo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 5d609fbf1..7a74c221f 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -678,7 +678,7 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) UFOUpdateDistanceToFinish(ufo); } - ufo_speed(ufo) = FixedMul(UFO_BASE_SPEED << 2, K_GetKartGameSpeedScalar(gamespeed)); + ufo_speed(ufo) = FixedMul(UFO_BASE_SPEED << 1, K_GetKartGameSpeedScalar(gamespeed)); // TODO: Adjustable Special Stage emerald color ufo->color = SKINCOLOR_CHAOSEMERALD1; From 630e5d4cf1ef2aad153e8f06ed58e22c00df2469 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 19 Dec 2022 02:00:46 -0500 Subject: [PATCH 31/40] Add less speed when damaging the UFO. Brings it back to old behavior before speed increase --- src/objects/ufo.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 7a74c221f..303b0bc9d 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -30,6 +30,8 @@ #define UFO_SPACING (768 * FRACUNIT) // How far the UFO wants to stay in front #define UFO_DEADZONE (2048 * FRACUNIT) // Deadzone where it won't update it's speed as much. #define UFO_SPEEDFACTOR (FRACUNIT * 3 / 4) // Factor of player's best speed, to make it more fair. +#define UFO_DAMAGED_SPEED (UFO_BASE_SPEED >> 1) // Speed to add when UFO takes damage. +#define UFO_START_SPEED (UFO_BASE_SPEED << 1) // Speed when the map starts. #define UFO_NUMARMS (3) #define UFO_ARMDELTA (ANGLE_MAX / UFO_NUMARMS) @@ -503,7 +505,7 @@ static UINT8 GetUFODamage(mobj_t *inflictor) boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType) { - const fixed_t addSpeed = FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed)); + const fixed_t addSpeed = FixedMul(UFO_DAMAGED_SPEED, K_GetKartGameSpeedScalar(gamespeed)); UINT8 damage = 1; (void)source; @@ -678,7 +680,7 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) UFOUpdateDistanceToFinish(ufo); } - ufo_speed(ufo) = FixedMul(UFO_BASE_SPEED << 1, K_GetKartGameSpeedScalar(gamespeed)); + ufo_speed(ufo) = FixedMul(UFO_START_SPEED, K_GetKartGameSpeedScalar(gamespeed)); // TODO: Adjustable Special Stage emerald color ufo->color = SKINCOLOR_CHAOSEMERALD1; From 7427daf7ad964c1c99fc881eefd42062a11f253f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 19 Dec 2022 02:10:11 -0500 Subject: [PATCH 32/40] Jawz uses Battle's steering in Special Stages Makes it miss the UFO's erratic movement less often --- src/objects/jawz.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/objects/jawz.c b/src/objects/jawz.c index 52a52a95c..5ad45735e 100644 --- a/src/objects/jawz.c +++ b/src/objects/jawz.c @@ -24,6 +24,7 @@ #include "../k_waypoint.h" #include "../k_respawn.h" #include "../k_collide.h" +#include "../k_specialstage.h" #define MAX_JAWZ_TURN (ANGLE_90 / 15) // We can turn a maximum of 6 degrees per frame at regular max speed @@ -185,6 +186,16 @@ static void JawzChase(mobj_t *th, boolean grounded) } } +static boolean JawzSteersBetter(void) +{ + if (specialStage.active == true) + { + return true; + } + + return !!!(gametyperules & GTR_CIRCUIT); +} + void Obj_JawzThink(mobj_t *th) { mobj_t *ghost = P_SpawnGhostMobj(th); @@ -215,7 +226,7 @@ void Obj_JawzThink(mobj_t *th) ghost->colorized = true; } - if (!(gametyperules & GTR_CIRCUIT)) + if (JawzSteersBetter() == true) { th->friction = max(0, 3 * th->friction / 4); } From 9d350c64c81d23754346ce3e254a8d872c059759 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 19 Dec 2022 04:49:53 -0500 Subject: [PATCH 33/40] Allow SPB to attack the UFO properly --- src/info.c | 2 +- src/k_objects.h | 1 + src/objects/spb.c | 181 +++++++++++++++++++++++++++++----------------- src/objects/ufo.c | 112 ++++++++++++++++++++-------- src/p_inter.c | 5 -- src/p_map.c | 47 ++++++++++++ 6 files changed, 243 insertions(+), 105 deletions(-) diff --git a/src/info.c b/src/info.c index 23d2a31c9..4566222f3 100644 --- a/src/info.c +++ b/src/info.c @@ -23631,7 +23631,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_kc64, // activesound - MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/k_objects.h b/src/k_objects.h index c0da8034f..284ec1c61 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -59,6 +59,7 @@ mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration); void Obj_BrolyKiThink(mobj_t *ki); /* Special Stage UFO */ +waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo); void Obj_SpecialUFOThinker(mobj_t *ufo); boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType); void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other); diff --git a/src/objects/spb.c b/src/objects/spb.c index 32cb0e12d..b76dfd4f8 100644 --- a/src/objects/spb.c +++ b/src/objects/spb.c @@ -23,6 +23,7 @@ #include "../z_zone.h" #include "../k_waypoint.h" #include "../k_respawn.h" +#include "../k_specialstage.h" #define SPB_SLIPTIDEDELTA (ANG1 * 3) #define SPB_STEERDELTA (ANGLE_90 - ANG10) @@ -292,7 +293,7 @@ static boolean SPBSeekSoundPlaying(mobj_t *spb) || S_SoundPlaying(spb, sfx_spbskc)); } -static void SPBSeek(mobj_t *spb, player_t *bestPlayer) +static void SPBSeek(mobj_t *spb, mobj_t *bestMobj) { const fixed_t desiredSpeed = SPB_DEFAULTSPEED; @@ -321,16 +322,15 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer) spb_lastplayer(spb) = -1; // Just make sure this is reset - if (bestPlayer == NULL - || bestPlayer->mo == NULL - || P_MobjWasRemoved(bestPlayer->mo) == true - || bestPlayer->mo->health <= 0 - || (bestPlayer->respawn.state != RESPAWNST_NONE)) + if (bestMobj == NULL + || P_MobjWasRemoved(bestMobj) == true + || bestMobj->health <= 0 + || (bestMobj->player != NULL && bestMobj->player->respawn.state != RESPAWNST_NONE)) { // No one there? Completely STOP. spb->momx = spb->momy = spb->momz = 0; - if (bestPlayer == NULL) + if (bestMobj == NULL) { spbplace = -1; } @@ -339,8 +339,16 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer) } // Found someone, now get close enough to initiate the slaughter... - P_SetTarget(&spb_chase(spb), bestPlayer->mo); - spbplace = bestPlayer->position; + P_SetTarget(&spb_chase(spb), bestMobj); + + if (bestMobj->player != NULL) + { + spbplace = bestMobj->player->position; + } + else + { + spbplace = 1; + } dist = SPBDist(spb, spb_chase(spb)); activeDist = FixedMul(SPB_ACTIVEDIST, spb_chase(spb)->scale); @@ -400,7 +408,18 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer) curWaypoint = K_GetWaypointFromIndex( (size_t)spb_curwaypoint(spb) ); } - destWaypoint = bestPlayer->nextwaypoint; + if (bestMobj->player != NULL) + { + destWaypoint = bestMobj->player->nextwaypoint; + } + else if (bestMobj->type == MT_SPECIAL_UFO) + { + destWaypoint = K_GetSpecialUFOWaypoint(bestMobj); + } + else + { + destWaypoint = K_GetBestWaypointForMobj(bestMobj); + } if (curWaypoint != NULL) { @@ -433,7 +452,8 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer) if (pathfindsuccess == true) { - if (cv_spbtest.value) { + if (cv_spbtest.value) + { if (pathtoplayer.numnodes > 1) { // Go to the next waypoint. @@ -529,45 +549,48 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer) SetSPBSpeed(spb, xySpeed, zSpeed); - // see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh! - steerDist = 1536 * mapobjectscale; - - for (i = 0; i < MAXPLAYERS; i++) + if (specialStage.active == false) { - fixed_t ourDist = INT32_MAX; - INT32 ourDelta = INT32_MAX; + // see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh! + steerDist = 1536 * mapobjectscale; - if (playeringame[i] == false || players[i].spectator == true) + for (i = 0; i < MAXPLAYERS; i++) { - // Not in-game - continue; + fixed_t ourDist = INT32_MAX; + INT32 ourDelta = INT32_MAX; + + if (playeringame[i] == false || players[i].spectator == true) + { + // Not in-game + continue; + } + + if (players[i].mo == NULL || P_MobjWasRemoved(players[i].mo) == true) + { + // Invalid mobj + continue; + } + + ourDelta = AngleDelta(spb->angle, R_PointToAngle2(spb->x, spb->y, players[i].mo->x, players[i].mo->y)); + if (ourDelta > SPB_STEERDELTA) + { + // Check if the angle wouldn't make us LOSE speed. + continue; + } + + ourDist = R_PointToDist2(spb->x, spb->y, players[i].mo->x, players[i].mo->y); + if (ourDist < steerDist) + { + steerDist = ourDist; + steerMobj = players[i].mo; // it doesn't matter if we override this guy now. + } } - if (players[i].mo == NULL || P_MobjWasRemoved(players[i].mo) == true) + // different player from our main target, try and ram into em~! + if (steerMobj != NULL && steerMobj != spb_chase(spb)) { - // Invalid mobj - continue; + P_Thrust(spb, R_PointToAngle2(spb->x, spb->y, steerMobj->x, steerMobj->y), spb_speed(spb) / 4); } - - ourDelta = AngleDelta(spb->angle, R_PointToAngle2(spb->x, spb->y, players[i].mo->x, players[i].mo->y)); - if (ourDelta > SPB_STEERDELTA) - { - // Check if the angle wouldn't make us LOSE speed. - continue; - } - - ourDist = R_PointToDist2(spb->x, spb->y, players[i].mo->x, players[i].mo->y); - if (ourDist < steerDist) - { - steerDist = ourDist; - steerMobj = players[i].mo; // it doesn't matter if we override this guy now. - } - } - - // different player from our main target, try and ram into em~! - if (steerMobj != NULL && steerMobj != spb_chase(spb)) - { - P_Thrust(spb, R_PointToAngle2(spb->x, spb->y, steerMobj->x, steerMobj->y), spb_speed(spb) / 4); } if (sliptide != 0) @@ -593,7 +616,7 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer) } } -static void SPBChase(mobj_t *spb, player_t *bestPlayer) +static void SPBChase(mobj_t *spb, mobj_t *bestMobj) { fixed_t baseSpeed = 0; fixed_t maxSpeed = 0; @@ -642,8 +665,8 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer) S_StartSound(spb, spb->info->activesound); } - // Maybe we want SPB to target an object later? IDK lol chasePlayer = chase->player; + if (chasePlayer != NULL) { UINT8 fracmax = 32; @@ -679,7 +702,8 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer) cy = chasePlayer->cmomy; // Switch targets if you're no longer 1st for long enough - if (bestPlayer != NULL && chasePlayer->position <= bestPlayer->position) + if (bestMobj != NULL + && (bestMobj->player == NULL || chasePlayer->position <= bestMobj->player->position)) { spb_modetimer(spb) = SPB_HOTPOTATO; } @@ -697,6 +721,12 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer) } } } + else + { + spb_lastplayer(spb) = -1; + spbplace = 1; + spb_modetimer(spb) = SPB_HOTPOTATO; + } dist = P_AproxDistance(P_AproxDistance(spb->x - chase->x, spb->y - chase->y), spb->z - chase->z); @@ -807,7 +837,7 @@ static void SPBWait(mobj_t *spb) void Obj_SPBThink(mobj_t *spb) { mobj_t *ghost = NULL; - player_t *bestPlayer = NULL; + mobj_t *bestMobj = NULL; UINT8 bestRank = UINT8_MAX; size_t i; @@ -844,6 +874,15 @@ void Obj_SPBThink(mobj_t *spb) } else { + if (specialStage.active == true) + { + if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false) + { + bestRank = 1; + bestMobj = specialStage.ufo; + } + } + // Find the player with the best rank for (i = 0; i < MAXPLAYERS; i++) { @@ -886,7 +925,7 @@ void Obj_SPBThink(mobj_t *spb) if (player->position < bestRank) { bestRank = player->position; - bestPlayer = player; + bestMobj = player->mo; } } @@ -894,11 +933,11 @@ void Obj_SPBThink(mobj_t *spb) { case SPB_MODE_SEEK: default: - SPBSeek(spb, bestPlayer); + SPBSeek(spb, bestMobj); break; case SPB_MODE_CHASE: - SPBChase(spb, bestPlayer); + SPBChase(spb, bestMobj); break; case SPB_MODE_WAIT: @@ -970,14 +1009,18 @@ void Obj_SPBExplode(mobj_t *spb) void Obj_SPBTouch(mobj_t *spb, mobj_t *toucher) { - player_t *player = toucher->player; + player_t *const player = toucher->player; + mobj_t *owner = NULL; + mobj_t *chase = NULL; if (spb_intangible(spb) > 0) { return; } - if ((spb_owner(spb) == toucher || spb_owner(spb) == toucher->target) + owner = spb_owner(spb); + + if ((owner == toucher || owner == toucher->target) && (spb_nothink(spb) > 0)) { return; @@ -988,30 +1031,34 @@ void Obj_SPBTouch(mobj_t *spb, mobj_t *toucher) return; } - if (player->spectator == true) + if (player != NULL) { - return; + if (player->spectator == true) + { + return; + } + + if (player->bubbleblowup > 0) + { + // Stun the SPB, and remove the shield. + K_DropHnextList(player, false); + spb_mode(spb) = SPB_MODE_WAIT; + spb_modetimer(spb) = 55; // Slightly over the respawn timer length + return; + } } - if (player->bubbleblowup > 0) - { - // Stun the SPB, and remove the shield. - K_DropHnextList(player, false); - spb_mode(spb) = SPB_MODE_WAIT; - spb_modetimer(spb) = 55; // Slightly over the respawn timer length - return; - } - - if (spb_chase(spb) != NULL && P_MobjWasRemoved(spb_chase(spb)) == false - && toucher == spb_chase(spb)) + chase = spb_chase(spb); + if (chase != NULL && P_MobjWasRemoved(chase) == false + && toucher == chase) { // Cause the explosion. Obj_SPBExplode(spb); return; } - else + else if (toucher->flags & MF_SHOOTABLE) { // Regular spinout, please. - P_DamageMobj(toucher, spb, spb_owner(spb), 1, DMG_NORMAL); + P_DamageMobj(toucher, spb, owner, 1, DMG_NORMAL); } } diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 303b0bc9d..96b2be1db 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -237,6 +237,25 @@ static void UFOUpdateAngle(mobj_t *ufo) ufo->angle += delta >> 2; } +waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo) +{ + if ((ufo == NULL) && (specialStage.active == true)) + { + ufo = specialStage.ufo; + } + + if (ufo != NULL && P_MobjWasRemoved(ufo) == false + && ufo->type == MT_SPECIAL_UFO) + { + if (ufo_waypoint(ufo) >= 0) + { + return K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo)); + } + } + + return NULL; +} + static void UFOMove(mobj_t *ufo) { waypoint_t *curWaypoint = NULL; @@ -256,11 +275,7 @@ static void UFOMove(mobj_t *ufo) boolean reachedEnd = false; - if (ufo_waypoint(ufo) >= 0) - { - curWaypoint = K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo)); - } - + curWaypoint = K_GetSpecialUFOWaypoint(ufo); destWaypoint = K_GetFinishLineWaypoint(); if (curWaypoint == NULL || destWaypoint == NULL) @@ -466,40 +481,74 @@ static void UFOKillPieces(mobj_t *ufo) } } -static UINT8 GetUFODamage(mobj_t *inflictor) +static UINT8 GetUFODamage(mobj_t *inflictor, UINT8 damageType) { - if (inflictor == NULL || P_MobjWasRemoved(inflictor) == true) + if (inflictor != NULL && P_MobjWasRemoved(inflictor) == false) { - // Default damage value. - return 10; + switch (inflictor->type) + { + case MT_JAWZ: + case MT_JAWZ_SHIELD: + case MT_ORBINAUT_SHIELD: + { + // Jawz / shields deal regular damage. + return 10; + } + case MT_ORBINAUT: + { + // Thrown orbinauts deal double damage. + return 20; + } + case MT_SPB: + { + // SPB deals triple damage. + return 30; + } + case MT_BANANA: + { + // Banana snipes deal triple damage, + // laid down bananas deal regular damage. + if (inflictor->health > 1) + { + return 30; + } + + return 10; + } + case MT_PLAYER: + { + // Players deal damage relative to how many sneakers they used. + return 15 * max(1, inflictor->player->numsneakers); + } + default: + { + break; + } + } } - switch (inflictor->type) + // Guess from damage type. + switch (damageType & DMG_TYPEMASK) { - case MT_SPB: - case MT_BANANA: - { - // SPB deals triple damage. - return 30; - } - case MT_PLAYER: - { - // Players deal damage relative to how many sneakers they used. - return 15 * inflictor->player->numsneakers; - } - case MT_ORBINAUT: - { - // Thrown orbinauts deal double damage. - return 20; - } - case MT_JAWZ: - case MT_JAWZ_SHIELD: - case MT_ORBINAUT_SHIELD: + case DMG_NORMAL: + case DMG_STING: default: { - // Jawz / shields deal regular damage. return 10; } + case DMG_WIPEOUT: + { + return 20; + } + case DMG_EXPLODE: + case DMG_TUMBLE: + { + return 30; + } + case DMG_VOLTAGE: + { + return 15; + } } } @@ -509,7 +558,6 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN UINT8 damage = 1; (void)source; - (void)damageType; if (UFOEmeraldChase(ufo) == true) { @@ -517,7 +565,7 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN return false; } - damage = GetUFODamage(inflictor); + damage = GetUFODamage(inflictor, damageType); if (damage <= 0) { diff --git a/src/p_inter.c b/src/p_inter.c index de1d0ef25..ca6612308 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -352,11 +352,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->target->player->karmadelay = comebacktime; } return; - case MT_SPB: - { - Obj_SPBTouch(special, toucher); - return; - } case MT_DUELBOMB: { Obj_DuelBombTouch(special, toucher); diff --git a/src/p_map.c b/src/p_map.c index 35d4bc431..04978c483 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -751,6 +751,53 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) // SRB2kart 011617 - Colission[sic] code for kart items //{ + if (thing->type == MT_SPB) + { + if (tm.thing->type != MT_PLAYER + && thing->tracer != tm.thing) + { + // Needs to be a player or the + // thing that we're chasing. + return BMIT_CONTINUE; + } + + if (tm.thing->z > thing->z + thing->height) + { + return BMIT_CONTINUE; // overhead + } + + if (tm.thing->z + tm.thing->height < thing->z) + { + return BMIT_CONTINUE; // underneath + } + + Obj_SPBTouch(thing, tm.thing); + return BMIT_CONTINUE; + } + else if (tm.thing->type == MT_SPB) + { + if (thing->type != MT_PLAYER + && tm.thing->tracer != thing) + { + // Needs to be a player or the + // thing that we're chasing. + return BMIT_CONTINUE; + } + + if (tm.thing->z > thing->z + thing->height) + { + return BMIT_CONTINUE; // overhead + } + + if (tm.thing->z + tm.thing->height < thing->z) + { + return BMIT_CONTINUE; // underneath + } + + Obj_SPBTouch(tm.thing, thing); + return BMIT_CONTINUE; + } + if (thing->type == MT_SHRINK_GUN || thing->type == MT_SHRINK_PARTICLE) { if (tm.thing->type != MT_PLAYER) From 138e6ba84665dd3181a79cca6fc9e04cd8a4d2bd Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 20 Dec 2022 19:39:50 -0500 Subject: [PATCH 34/40] Break the Capsules is always Time Attack rules --- src/k_kart.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index e664ec624..cc7b1453f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -525,6 +525,14 @@ boolean K_TimeAttackRules(void) return true; } + if (battlecapsules == true) + { + // Break the Capsules always uses Time Attack + // rules, since you can bring 2-4 players in + // via Grand Prix. + return true; + } + for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] == false || players[i].spectator == true) From 0502affedbd4231ae267c4abf3ba121261d4aa9c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 20 Dec 2022 19:44:49 -0500 Subject: [PATCH 35/40] Remove offset copy from overlays --- src/p_mobj.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index 2ac670b93..c028dcbd9 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5298,6 +5298,7 @@ void P_RunOverlays(void) mo->pitch = mo->target->pitch; mo->roll = mo->target->roll; +#if 0 mo->spritexoffset = mo->target->spritexoffset; mo->spriteyoffset = mo->target->spriteyoffset; mo->spritexscale = mo->target->spritexscale; @@ -5306,6 +5307,7 @@ void P_RunOverlays(void) mo->sprxoff = mo->target->sprxoff; mo->spryoff = mo->target->spryoff; mo->sprzoff = mo->target->sprzoff; +#endif mo->hitlag = mo->target->hitlag; mo->eflags = (mo->eflags & ~MFE_DAMAGEHITLAG) | (mo->target->eflags & MFE_DAMAGEHITLAG); From 07767c4d039e6e54740ffe851e3ef38392ecee70 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 20 Dec 2022 19:46:22 -0500 Subject: [PATCH 36/40] No reference count update in K_ResetSpecialStage --- src/k_specialstage.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/k_specialstage.c b/src/k_specialstage.c index 3700d98fb..e0551bef1 100644 --- a/src/k_specialstage.c +++ b/src/k_specialstage.c @@ -31,7 +31,6 @@ struct specialStage specialStage; --------------------------------------------------*/ void K_ResetSpecialStage(void) { - P_SetTarget(&specialStage.ufo, NULL); memset(&specialStage, 0, sizeof(struct specialStage)); } From df96c633bc44de8d366b65b7614f2900e7e894a6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 20 Dec 2022 19:47:20 -0500 Subject: [PATCH 37/40] Set Battle for Boss warp --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index da7e87d1c..ab4a070a1 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2881,7 +2881,7 @@ static void Command_Map_f(void) if (mapheaderinfo[newmapnum-1]) { // Let's just guess so we don't have to specify the gametype EVERY time... - newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & TOL_BATTLE) ? GT_BATTLE : GT_RACE; + newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & (TOL_BATTLE|TOL_BOSS)) ? GT_BATTLE : GT_RACE; } } From 5461abb022199f2dc590bf1f307d1ecaa5ded40e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 20 Dec 2022 19:51:24 -0500 Subject: [PATCH 38/40] Always update ufo_pieces if possible --- src/objects/ufo.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 96b2be1db..7e80297a4 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -688,16 +688,16 @@ void Obj_UFOPieceRemoved(mobj_t *piece) &ufo_piece_prev(next), (prev != NULL && P_MobjWasRemoved(prev) == false) ? prev : NULL ); + } - if (ufo != NULL && P_MobjWasRemoved(ufo) == false) + if (ufo != NULL && P_MobjWasRemoved(ufo) == false) + { + if (piece == ufo_pieces(ufo)) { - if (piece == ufo_pieces(ufo)) - { - P_SetTarget( - &ufo_pieces(ufo), - next - ); - } + P_SetTarget( + &ufo_pieces(ufo), + (next != NULL && P_MobjWasRemoved(next) == false) ? next : NULL + ); } } From 19fd6d12e28f7b2ca1d7aa9565a2417aa4b5a5d1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 20 Dec 2022 19:53:19 -0500 Subject: [PATCH 39/40] Rebalance Jawz Jawz have their old missable handling, but now deal more damage. --- src/objects/jawz.c | 5 ----- src/objects/ufo.c | 8 ++++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/objects/jawz.c b/src/objects/jawz.c index 5ad45735e..0f952551a 100644 --- a/src/objects/jawz.c +++ b/src/objects/jawz.c @@ -188,11 +188,6 @@ static void JawzChase(mobj_t *th, boolean grounded) static boolean JawzSteersBetter(void) { - if (specialStage.active == true) - { - return true; - } - return !!!(gametyperules & GTR_CIRCUIT); } diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 7e80297a4..5eaf7f645 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -487,13 +487,17 @@ static UINT8 GetUFODamage(mobj_t *inflictor, UINT8 damageType) { switch (inflictor->type) { - case MT_JAWZ: case MT_JAWZ_SHIELD: case MT_ORBINAUT_SHIELD: { - // Jawz / shields deal regular damage. + // Shields deal chip damage. return 10; } + case MT_JAWZ: + { + // Thrown Jawz deal a bit extra. + return 15; + } case MT_ORBINAUT: { // Thrown orbinauts deal double damage. From 86c3a8ab78cee4cb5e4649df9ba12f383e1b0165 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 22 Dec 2022 05:35:35 -0500 Subject: [PATCH 40/40] Fix Gachabom merge --- src/k_roulette.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index b747da9d7..4f3de015f 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -166,12 +166,14 @@ static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] = { 0, 0, 0, 0 }, // Kitchen Sink { 0, 0, 0, 0 }, // Drop Target { 0, 0, 0, 0 }, // Garden Top + { 0, 0, 0, 0 }, // Gachabom { 0, 1, 1, 0 }, // Sneaker x2 { 0, 0, 1, 1 }, // Sneaker x3 { 0, 0, 0, 0 }, // Banana x3 { 0, 1, 1, 0 }, // Orbinaut x3 { 0, 0, 1, 1 }, // Orbinaut x4 - { 0, 0, 1, 1 } // Jawz x2 + { 0, 0, 1, 1 }, // Jawz x2 + { 0, 0, 0, 0 } // Gachabom x3 }; static kartitems_t K_KartItemReelTimeAttack[] =