From 8ca3d41696d3c3428bf5209be840585c0087f884 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 21 Nov 2022 23:51:26 -0500 Subject: [PATCH 01/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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; }