diff --git a/src/deh_tables.c b/src/deh_tables.c index d58550367..dd97d1825 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5336,6 +5336,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_HYUDORO", "MT_HYUDORO_CENTER", + "MT_SHRINK_POHBEE", + "MT_SINK", // Kitchen Sink Stuff "MT_SINK_SHIELD", "MT_SINKTRAIL", diff --git a/src/info.c b/src/info.c index 72665927e..f7135472a 100644 --- a/src/info.c +++ b/src/info.c @@ -24009,6 +24009,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SHRINK_POHBEE + -1, // doomednum + S_HYUDORO, // 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 + 32*FRACUNIT, // radius + 24*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_SINK -1, // doomednum S_SINK, // spawnstate diff --git a/src/info.h b/src/info.h index ab1867973..5d96a4317 100644 --- a/src/info.h +++ b/src/info.h @@ -6362,6 +6362,8 @@ typedef enum mobj_type MT_HYUDORO, MT_HYUDORO_CENTER, + MT_SHRINK_POHBEE, + MT_SINK, // Kitchen Sink Stuff MT_SINK_SHIELD, MT_SINKTRAIL, diff --git a/src/k_kart.c b/src/k_kart.c index ab8f04da9..08d3fc1a9 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5581,46 +5581,12 @@ void K_DoSneaker(player_t *player, INT32 type) static void K_DoShrink(player_t *user) { - INT32 i; mobj_t *mobj, *next; S_StartSound(user->mo, sfx_kc46); // Sound the BANG! user->pflags |= PF_ATTACKDOWN; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - if (&players[i] == user) - continue; - if (players[i].position < user->position) - { - //P_FlashPal(&players[i], PAL_NUKE, 10); - - // Grow should get taken away. - if (players[i].growshrinktimer > 0) - K_RemoveGrowShrink(&players[i]); - else - { - // Start shrinking! - K_DropItems(&players[i]); - players[i].growshrinktimer = -(15*TICRATE); - - if (players[i].mo && !P_MobjWasRemoved(players[i].mo)) - { - players[i].mo->scalespeed = mapobjectscale/TICRATE; - players[i].mo->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); - - if (K_PlayerShrinkCheat(&players[i]) == true) - { - players[i].mo->destscale = FixedMul(players[i].mo->destscale, SHRINK_SCALE); - } - - S_StartSound(players[i].mo, sfx_kc59); - } - } - } - } + Obj_CreateShrinkPohbees(user); // kill everything in the kitem list while we're at it: for (mobj = kitemcap; mobj; mobj = next) diff --git a/src/k_objects.h b/src/k_objects.h index 45f9df78c..d0b6f6b8b 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -8,4 +8,8 @@ void Obj_HyudoroThink(mobj_t *actor); void Obj_HyudoroCenterThink(mobj_t *actor); void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher); +/* Shrink */ +void Obj_PohbeeThinker(mobj_t *pohbee); +void Obj_CreateShrinkPohbees(player_t *owner); + #endif/*k_objects_H*/ diff --git a/src/k_waypoint.c b/src/k_waypoint.c index a6a592a6b..0a8dfd0f0 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -2044,6 +2044,7 @@ boolean K_SetupWaypointList(void) // Loop through the waypointcap here so that all waypoints are added to the heap, and allow easier debugging for (waypointmobj = waypointcap; waypointmobj; waypointmobj = waypointmobj->tracer) { + waypointmobj->cusval = (INT32)numwaypoints; K_SetupWaypoint(waypointmobj); } diff --git a/src/objects/Sourcefile b/src/objects/Sourcefile index f7e4f2491..94f7dd25b 100644 --- a/src/objects/Sourcefile +++ b/src/objects/Sourcefile @@ -1 +1,2 @@ hyudoro.c +shrink.c diff --git a/src/objects/shrink.c b/src/objects/shrink.c new file mode 100644 index 000000000..b6e41111b --- /dev/null +++ b/src/objects/shrink.c @@ -0,0 +1,347 @@ +// 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 POHBEE_HOVER (300 << FRACBITS) +#define POHBEE_SPEED (128 << FRACBITS) +#define POHBEE_TIME (15 * TICRATE) +#define POHBEE_DIST (2048 << FRACBITS) +#define POHBEE_CHAIN (16) + +#define LASER_SPEED (20 << FRACBITS) + +enum +{ + POHBEE_MODE_SPAWN, + POHBEE_MODE_ACT, + POHBEE_MODE_DESPAWN, +}; + +#define pohbee_mode(o) ((o)->cusval) + +#define pohbee_waypoint_cur(o) ((o)->extravalue1) +#define pohbee_waypoint_dest(o) ((o)->extravalue2) + +#define pohbee_owner(o) ((o)->target) +#define pohbee_laser(o) ((o)->tracer) +#define pohbee_chain(o) ((o)->hnext) + +#define laser_owner(o) ((o)->target) +#define laser_pohbee(o) ((o)->tracer) + +static void PohbeeMoveTo(mobj_t *pohbee, fixed_t destx, fixed_t desty, fixed_t destz) +{ + pohbee->momx = destx - pohbee->x; + pohbee->momy = desty - pohbee->y; + pohbee->momz = destz - pohbee->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 fixed_t PohbeeWaypointZ(mobj_t *dest) +{ + return dest->z + (FixedMul(POHBEE_HOVER, mapobjectscale) * P_MobjFlip(dest)); +} + +static void PohbeeSpawn(mobj_t *pohbee) +{ + waypoint_t *curWaypoint = NULL; + waypoint_t *destWaypoint = NULL; + + fixed_t distLeft = INT32_MAX; + fixed_t newX = pohbee->x; + fixed_t newY = pohbee->y; + fixed_t newZ = pohbee->z; + + boolean finalize = false; + + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + size_t pathIndex = 0; + + curWaypoint = K_GetWaypointFromIndex((size_t)pohbee_waypoint_cur(pohbee)); + destWaypoint = K_GetWaypointFromIndex((size_t)pohbee_waypoint_dest(pohbee)); + + if (curWaypoint == NULL || destWaypoint == NULL) + { + // Waypoints aren't valid. + // Just transition into the next state. + pohbee_mode(pohbee) = POHBEE_MODE_ACT; + return; + } + + distLeft = FixedMul(POHBEE_SPEED, mapobjectscale); + + while (distLeft > 0) + { + fixed_t wpX = curWaypoint->mobj->x; + fixed_t wpY = curWaypoint->mobj->y; + fixed_t wpZ = PohbeeWaypointZ(curWaypoint->mobj); + + 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. + finalize = true; + 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. + finalize = true; + break; + } + } + + pathIndex++; + + if (pathIndex >= pathtofinish.numnodes) + { + // Successfully reached the end of the path. + finalize = true; + break; + } + + // Now moving to the next waypoint. + curWaypoint = (waypoint_t *)pathtofinish.array[pathIndex].nodedata; + pohbee_waypoint_cur(pohbee) = (INT32)K_GetWaypointHeapIndex(curWaypoint); + } + } + + PohbeeMoveTo(pohbee, newX, newY, newZ); + + if (finalize == true) + { + pohbee_mode(pohbee) = POHBEE_MODE_ACT; + } + + if (pathfindsuccess == true) + { + Z_Free(pathtofinish.array); + } +} + +void Obj_PohbeeThinker(mobj_t *pohbee) +{ + pohbee->momx = 0; + pohbee->momy = 0; + pohbee->momz = 0; + + switch (pohbee_mode(pohbee)) + { + case POHBEE_MODE_SPAWN: + PohbeeSpawn(pohbee); + break; + + case POHBEE_MODE_ACT: + //PohbeeSpawn(pohbee); + break; + + case POHBEE_MODE_DESPAWN: + //PohbeeSpawn(pohbee); + break; + + default: + // failsafe + pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; + break; + } +} + +static void CreatePohbeeForPlayer(player_t *target, player_t *owner) +{ + const UINT32 traveldist = FixedMul(POHBEE_DIST, mapobjectscale) / FRACUNIT; + + mobj_t *pohbee = NULL; + //mobj_t *laser = NULL; + + waypoint_t *startWaypoint = NULL; + waypoint_t *endWaypoint = NULL; + + I_Assert(target != NULL); + I_Assert(owner != NULL); + + if (target->mo == NULL || P_MobjWasRemoved(target->mo) == true) + { + // No object for the target. + return; + } + + // First, go backwards to find our starting point. + { + const boolean useshortcuts = false; + const boolean huntbackwards = true; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + + pathfindsuccess = K_PathfindThruCircuit( + target->nextwaypoint, traveldist, + &pathtofinish, + useshortcuts, huntbackwards + ); + + if (pathfindsuccess == true) + { + startWaypoint = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata; + Z_Free(pathtofinish.array); + } + else + { + startWaypoint = target->nextwaypoint; + } + } + + // Now, go forwards to get our ending point. + { + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + + pathfindsuccess = K_PathfindThruCircuit( + target->nextwaypoint, traveldist * 2, + &pathtofinish, + useshortcuts, huntbackwards + ); + + if (pathfindsuccess == true) + { + endWaypoint = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata; + Z_Free(pathtofinish.array); + } + else + { + endWaypoint = target->nextwaypoint; + } + } + + // Try to repair an incomplete pair, just in case. + if (startWaypoint == NULL && endWaypoint != NULL) + { + startWaypoint = endWaypoint; + } + + if (endWaypoint == NULL && startWaypoint != NULL) + { + endWaypoint = startWaypoint; + } + + if (startWaypoint == NULL || endWaypoint == NULL) + { + // Unable to create shrink lasers + // due to invalid waypoint structure... + return; + } + + // Valid spawning conditions, + // we can start creating each individual part. + pohbee = P_SpawnMobjFromMobj(startWaypoint->mobj, 0, 0, POHBEE_HOVER, MT_SHRINK_POHBEE); + + P_SetTarget(&pohbee_owner(pohbee), owner->mo); + + pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; + + pohbee_waypoint_cur(pohbee) = (INT32)K_GetWaypointHeapIndex(startWaypoint); + pohbee_waypoint_dest(pohbee) = (INT32)K_GetWaypointHeapIndex(endWaypoint); +} + +void Obj_CreateShrinkPohbees(player_t *owner) +{ + UINT8 ownerPos = 1; + UINT8 i; + + I_Assert(owner != NULL); + + ownerPos = owner->position; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *player = NULL; + + if (playeringame[i] == false) + { + // Not valid. + continue; + } + + player = &players[i]; + + if (player->spectator == true) + { + // Not playing. + continue; + } + + if (player->position > ownerPos) + { + // Too far behind. + continue; + } + + CreatePohbeeForPlayer(player, owner); + } +} diff --git a/src/p_mobj.c b/src/p_mobj.c index 621da9d9f..f2aebb6d7 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7841,6 +7841,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_HyudoroCenterThink(mobj); break; } + case MT_SHRINK_POHBEE: + { + Obj_PohbeeThinker(mobj); + break; + } case MT_ROCKETSNEAKER: if (!mobj->target || !mobj->target->health) {