diff --git a/src/d_player.h b/src/d_player.h index 91989279c..5675dfb5e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -584,6 +584,8 @@ typedef struct player_s UINT8 stairjank; + UINT8 shrinkLaserDelay; + #ifdef HWRENDER fixed_t fovadd; // adjust FOV for hw rendering #endif diff --git a/src/deh_tables.c b/src/deh_tables.c index 18266231e..5fe950e9e 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3755,6 +3755,15 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi // Caked-Up Booty-Sheet Ghost "S_HYUDORO", + // Grow + "S_GROW_PARTICLE", + + // Shrink + "S_SHRINK_GUN", + "S_SHRINK_CHAIN", + "S_SHRINK_LASER", + "S_SHRINK_PARTICLE", + // The legend "S_SINK", "S_SINK_SHIELD", @@ -5337,6 +5346,14 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_HYUDORO", "MT_HYUDORO_CENTER", + "MT_GROW_PARTICLE", + + "MT_SHRINK_POHBEE", + "MT_SHRINK_GUN", + "MT_SHRINK_CHAIN", + "MT_SHRINK_LASER", + "MT_SHRINK_PARTICLE", + "MT_SINK", // Kitchen Sink Stuff "MT_SINK_SHIELD", "MT_SINKTRAIL", diff --git a/src/info.c b/src/info.c index 858fdab3c..186f04022 100644 --- a/src/info.c +++ b/src/info.c @@ -573,6 +573,9 @@ char sprnames[NUMSPRITES + 1][5] = "FLML", // Flame Shield speed lines "FLMF", // Flame Shield flash "HYUU", // Hyudoro + "GRWP", // Grow + "POHB", // Shrink Poh-Bee + "SHRG", // Shrink gun / laser "SINK", // Kitchen Sink "SITR", // Kitchen Sink Trail "KBLN", // Battle Mode Bumper @@ -4314,6 +4317,13 @@ state_t states[NUMSTATES] = {SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO + {SPR_GRWP, FF_FULLBRIGHT|FF_ANIMATE, 13, {NULL}, 7, 1, S_NULL}, // S_GROW_PARTICLE + + {SPR_SHRG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_GUN + {SPR_POHB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_CHAIN + {SPR_SHRG, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_LASER + {SPR_SHRG, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_PARTICLE + {SPR_SINK, 0, 1, {A_SmokeTrailer}, MT_SINKTRAIL, 0, S_SINK}, // S_SINK {SPR_SINK, 0|FF_TRANS80|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_SINK_SHIELD}, // S_SINK_SHIELD {SPR_SITR, 0, 1, {NULL}, 0, 0, S_SINKTRAIL2}, // S_SINKTRAIL1 @@ -24036,6 +24046,168 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_GROW_PARTICLE + -1, // doomednum + S_GROW_PARTICLE, // 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 + 4*FRACUNIT, // radius + 8*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + 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_SHRINK_GUN + -1, // doomednum + S_SHRINK_GUN, // 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 + 52*FRACUNIT, // radius + 120*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_SHRINK_CHAIN + -1, // doomednum + S_SHRINK_CHAIN, // 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 + 26*FRACUNIT, // radius + 26*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_SHRINK_LASER + -1, // doomednum + S_SHRINK_LASER, // 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 + 16*FRACUNIT, // radius + 33*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_SHRINK_PARTICLE + -1, // doomednum + S_SHRINK_PARTICLE, // 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 + 26*FRACUNIT, // radius + 26*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOCLIP|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 50d233aff..8acf73ba8 100644 --- a/src/info.h +++ b/src/info.h @@ -1119,6 +1119,9 @@ typedef enum sprite SPR_FLML, // Flame Shield speed lines SPR_FLMF, // Flame Shield flash SPR_HYUU, // Hyudoro + SPR_GRWP, // Grow + SPR_POHB, // Shrink Poh-Bee + SPR_SHRG, // Shrink gun / laser SPR_SINK, // Kitchen Sink SPR_SITR, // Kitchen Sink Trail SPR_KBLN, // Battle Mode Bumper @@ -4745,6 +4748,15 @@ typedef enum state // Caked-Up Booty-Sheet Ghost S_HYUDORO, + // Grow + S_GROW_PARTICLE, + + // Shrink + S_SHRINK_GUN, + S_SHRINK_CHAIN, + S_SHRINK_LASER, + S_SHRINK_PARTICLE, + // The legend S_SINK, S_SINK_SHIELD, @@ -6363,6 +6375,14 @@ typedef enum mobj_type MT_HYUDORO, MT_HYUDORO_CENTER, + MT_GROW_PARTICLE, + + MT_SHRINK_POHBEE, + MT_SHRINK_GUN, + MT_SHRINK_CHAIN, + MT_SHRINK_LASER, + MT_SHRINK_PARTICLE, + MT_SINK, // Kitchen Sink Stuff MT_SINK_SHIELD, MT_SINKTRAIL, diff --git a/src/k_botsearch.c b/src/k_botsearch.c index af7464c30..74a458bce 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -425,6 +425,16 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) case MT_BUBBLESHIELDTRAP: K_AddDodgeObject(thing, side, 20); break; + case MT_SHRINK_GUN: + if (thing->target == globalsmuggle.botmo) + { + K_AddAttackObject(thing, side, 20); + } + else + { + K_AddDodgeObject(thing, side, 20); + } + break; case MT_RANDOMITEM: if (anglediff >= 45) { diff --git a/src/k_kart.c b/src/k_kart.c index 5683baa6f..edefc6895 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -355,7 +355,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = //P-Odds 0 1 2 3 4 5 6 7 /*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker - /*Invincibility*/ { 0, 0, 0, 0, 3, 4, 6, 9 }, // Invincibility + /*Invincibility*/ { 0, 0, 0, 0, 3, 4, 5, 7 }, // Invincibility /*Banana*/ { 2, 3, 1, 0, 0, 0, 0, 0 }, // Banana /*Eggman Monitor*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor /*Orbinaut*/ { 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut @@ -365,7 +365,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Ballhog*/ { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow - /*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink + /*Shrink*/ { 0, 0, 0, 0, 0, 1, 3, 2 }, // Shrink /*Lightning Shield*/ { 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield /*Bubble Shield*/ { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield @@ -375,7 +375,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink /*Drop Target*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target /*Sneaker x2*/ { 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2 - /*Sneaker x3*/ { 0, 0, 0, 1, 6,10, 5, 0 }, // Sneaker x3 + /*Sneaker x3*/ { 0, 0, 0, 1, 6, 9, 5, 0 }, // Sneaker x3 /*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 /*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 /*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 @@ -2284,6 +2284,77 @@ void K_SpawnInvincibilitySpeedLines(mobj_t *mo) fast->destscale = 6*((mo->player->invincibilitytimer/TICRATE)*FRACUNIT)/8; } +static void K_SpawnGrowShrinkParticles(mobj_t *mo, INT32 timer) +{ + const boolean shrink = (timer < 0); + const INT32 maxTime = (10*TICRATE); + const INT32 noTime = (2*TICRATE); + INT32 spawnFreq = 1; + + mobj_t *particle = NULL; + fixed_t particleScale = FRACUNIT; + fixed_t particleSpeed = 0; + + spawnFreq = abs(timer); + + if (spawnFreq < noTime) + { + return; + } + + spawnFreq -= noTime; + + if (spawnFreq > maxTime) + { + spawnFreq = maxTime; + } + + spawnFreq = (maxTime - spawnFreq) / TICRATE / 4; + if (spawnFreq == 0) + { + spawnFreq++; + } + + if (leveltime % spawnFreq != 0) + { + return; + } + + particle = P_SpawnMobjFromMobj( + mo, + P_RandomRange(-32, 32) * FRACUNIT, + P_RandomRange(-32, 32) * FRACUNIT, + (P_RandomRange(0, 24) + (shrink ? 48 : 0)) * FRACUNIT, + MT_GROW_PARTICLE + ); + + P_SetTarget(&particle->target, mo); + + particle->momx = mo->momx; + particle->momy = mo->momy; + particle->momz = P_GetMobjZMovement(mo); + + K_MatchGenericExtraFlags(particle, mo); + + particleScale = FixedMul((shrink ? SHRINK_PHYSICS_SCALE : GROW_PHYSICS_SCALE), mapobjectscale); + particleSpeed = mo->scale * 4 * P_MobjFlip(mo); // NOT particleScale + + particle->destscale = particleScale; + P_SetScale(particle, particle->destscale); + + if (shrink == true) + { + particle->color = SKINCOLOR_KETCHUP; + particle->momz -= particleSpeed; + particle->renderflags |= RF_VERTICALFLIP; + } + else + { + particle->color = SKINCOLOR_SAPPHIRE; + particle->momz += particleSpeed; + } +} + void K_SpawnBumpEffect(mobj_t *mo) { mobj_t *fx = P_SpawnMobj(mo->x, mo->y, mo->z, MT_BUMP); @@ -3643,7 +3714,7 @@ void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 typ P_SetPlayerMobjState(player->mo, S_KART_SPINOUT); } -static void K_RemoveGrowShrink(player_t *player) +void K_RemoveGrowShrink(player_t *player) { if (player->mo && !P_MobjWasRemoved(player->mo)) { @@ -5582,46 +5653,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) @@ -7359,6 +7396,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } + if (player->growshrinktimer != 0) + { + K_SpawnGrowShrinkParticles(player->mo, player->growshrinktimer); + } + if (gametype == GT_RACE && player->rings <= 0) // spawn ring debt indicator { mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy, @@ -7523,6 +7565,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) comebackshowninfo = true; // client has already seen the message } + if (player->shrinkLaserDelay) + player->shrinkLaserDelay--; + if (player->ringdelay) player->ringdelay--; @@ -10019,7 +10064,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } // TODO: gametyperules - player->growshrinktimer = (gametype == GT_BATTLE ? 8 : 12) * TICRATE; + player->growshrinktimer = max(player->growshrinktimer, (gametype == GT_BATTLE ? 8 : 12) * TICRATE); if (player->invincibilitytimer > 0) { diff --git a/src/k_kart.h b/src/k_kart.h index bab501200..4529b2611 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -71,6 +71,7 @@ void K_AwardPlayerRings(player_t *player, INT32 rings, boolean overload); void K_DoInstashield(player_t *player); void K_DoPowerClash(player_t *t1, player_t *t2); void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved); +void K_RemoveGrowShrink(player_t *player); void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type); void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_TumbleInterrupt(player_t *player); diff --git a/src/k_objects.h b/src/k_objects.h index 45f9df78c..cc80d2555 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -8,4 +8,11 @@ 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_PohbeeRemoved(mobj_t *pohbee); +void Obj_ShrinkGunRemoved(mobj_t *gun); +boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim); +void Obj_CreateShrinkPohbees(player_t *owner); + #endif/*k_objects_H*/ diff --git a/src/k_waypoint.c b/src/k_waypoint.c index a6a592a6b..2c9ebec9a 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1101,6 +1101,40 @@ static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData) return scoreReached; } +/*-------------------------------------------------- + static boolean K_WaypointPathfindReachedGScoreSpawnable(void *data, void *setupData) + + Returns if the current waypoint data reaches our end G score. + + 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 reached the G score, false otherwise. +--------------------------------------------------*/ +static boolean K_WaypointPathfindReachedGScoreSpawnable(void *data, void *setupData) +{ + boolean scoreReached = false; + boolean spawnable = false; + + if (data == NULL || setupData == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindReachedGScoreSpawnable received NULL data.\n"); + } + else + { + pathfindnode_t *node = (pathfindnode_t *)data; + pathfindsetup_t *setup = (pathfindsetup_t *)setupData; + waypoint_t *wp = (waypoint_t *)node->nodedata; + + scoreReached = (node->gscore >= setup->endgscore); + spawnable = K_GetWaypointIsSpawnpoint(wp); + } + + return (scoreReached && spawnable); +} + /*-------------------------------------------------- boolean K_PathfindToWaypoint( waypoint_t *const sourcewaypoint, @@ -1266,6 +1300,89 @@ boolean K_PathfindThruCircuit( return pathfound; } +/*-------------------------------------------------- + boolean K_PathfindThruCircuitSpawnable( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) + + See header file for description. +--------------------------------------------------*/ +boolean K_PathfindThruCircuitSpawnable( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) +{ + boolean pathfound = false; + + if (sourcewaypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL sourcewaypoint in K_PathfindThruCircuitSpawnable.\n"); + } + else if (finishline == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL finishline in K_PathfindThruCircuitSpawnable.\n"); + } + else if (((huntbackwards == false) && (sourcewaypoint->numnextwaypoints == 0)) + || ((huntbackwards == true) && (sourcewaypoint->numprevwaypoints == 0))) + { + CONS_Debug(DBG_GAMELOGIC, + "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}; + getconnectednodesfunc nextnodesfunc = K_WaypointPathfindGetNext; + getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts; + getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic; + getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts; + getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedGScoreSpawnable; + + if (huntbackwards) + { + nextnodesfunc = K_WaypointPathfindGetPrev; + nodecostsfunc = K_WaypointPathfindGetPrevCosts; + } + + if (useshortcuts) + { + traversablefunc = K_WaypointPathfindTraversableAllEnabled; + } + + pathfindsetup.opensetcapacity = K_GetOpensetBaseSize(); + pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize(); + pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize(); + pathfindsetup.startnodedata = sourcewaypoint; + pathfindsetup.endnodedata = finishline; + pathfindsetup.endgscore = traveldistance; + pathfindsetup.getconnectednodes = nextnodesfunc; + pathfindsetup.getconnectioncosts = nodecostsfunc; + pathfindsetup.getheuristic = heuristicfunc; + pathfindsetup.gettraversable = traversablefunc; + pathfindsetup.getfinished = finishedfunc; + + pathfound = K_PathfindAStar(returnpath, &pathfindsetup); + + K_UpdateOpensetBaseSize(pathfindsetup.opensetcapacity); + K_UpdateClosedsetBaseSize(pathfindsetup.closedsetcapacity); + K_UpdateNodesArrayBaseSize(pathfindsetup.nodesarraycapacity); + } + + return pathfound; +} + /*-------------------------------------------------- waypoint_t *K_GetNextWaypointToDestination( waypoint_t *const sourcewaypoint, @@ -2044,6 +2161,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/k_waypoint.h b/src/k_waypoint.h index a2201ff26..1cb659dbe 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -245,6 +245,36 @@ boolean K_PathfindThruCircuit( const boolean huntbackwards); +/*-------------------------------------------------- + boolean K_PathfindThruCircuitSpawnable( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) + + The same as K_PathfindThruCircuit, but continues until hitting a waypoint that + can be respawned at. + + Input Arguments:- + sourcewaypoint - The waypoint to start searching from + traveldistance - How far along the circuit it will try to pathfind. + returnpath - The path_t that will contain the final found path + useshortcuts - Whether to use waypoints that are marked as being shortcuts in the search + huntbackwards - Goes through the waypoints backwards if true + + Return:- + True if a circuit path could be constructed, false if it couldn't. +--------------------------------------------------*/ + +boolean K_PathfindThruCircuitSpawnable( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards); + + /*-------------------------------------------------- waypoint_t *K_GetNextWaypointToDestination( waypoint_t *const sourcewaypoint, 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..0728385cf --- /dev/null +++ b/src/objects/shrink.c @@ -0,0 +1,771 @@ +// 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 (256 << FRACBITS) +#define POHBEE_SPEED (128 << FRACBITS) +#define POHBEE_TIME (30 * TICRATE) +#define POHBEE_DIST (4096 << FRACBITS) + +#define GUN_SWING (ANGLE_90 - ANG10) +#define GUN_SWINGTIME (4 * TICRATE) + +#define CHAIN_SIZE (52) + +#define EXTRA_FOR_FIRST (7) + +enum +{ + POHBEE_MODE_SPAWN, + POHBEE_MODE_ACT, + POHBEE_MODE_DESPAWN, +}; + +#define pohbee_mode(o) ((o)->cusval) +#define pohbee_timer(o) ((o)->reactiontime) +#define pohbee_waypoint_cur(o) ((o)->extravalue1) +#define pohbee_waypoint_dest(o) ((o)->extravalue2) +#define pohbee_height(o) ((o)->movefactor) + +#define pohbee_owner(o) ((o)->target) +#define pohbee_guns(o) ((o)->hnext) + +#define gun_offset(o) ((o)->movecount) +#define gun_numsegs(o) ((o)->extravalue1) + +#define gun_pohbee(o) ((o)->target) +#define gun_laser(o) ((o)->tracer) +#define gun_chains(o) ((o)->hprev) + +#define chain_index(o) ((o)->extravalue1) + +enum +{ + LASER_SHRINK, + LASER_GROW, +}; + +static skincolornum_t ShrinkLaserColor(mobj_t *pohbee) +{ + UINT8 laserState = LASER_SHRINK; + player_t *owner = NULL; + + if (pohbee_owner(pohbee) != NULL && P_MobjWasRemoved(pohbee_owner(pohbee)) == false) + { + owner = pohbee_owner(pohbee)->player; + } + + if (owner != NULL && P_IsDisplayPlayer(owner) == true) + { + laserState = LASER_GROW; + + if (r_splitscreen > 0 && (leveltime & 1)) + { + // TODO: make this properly screen dependent, + // instead of flashing. + laserState = LASER_SHRINK; + } + } + + switch (laserState) + { + default: + case LASER_SHRINK: + return SKINCOLOR_KETCHUP; + + case LASER_GROW: + return SKINCOLOR_SAPPHIRE; + } +} + +static boolean ShrinkLaserActive(mobj_t *pohbee) +{ + return (pohbee_mode(pohbee) == POHBEE_MODE_ACT); +} + +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 *pohbee, mobj_t *dest) +{ + return dest->z + (pohbee_height(pohbee) + 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(pohbee, 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); + pohbee->angle = K_MomentumAngle(pohbee); + + if (finalize == true) + { + // Move to next state + pohbee_mode(pohbee) = POHBEE_MODE_ACT; + } + + if (pathfindsuccess == true) + { + Z_Free(pathtofinish.array); + } +} + +static void PohbeeAct(mobj_t *pohbee) +{ + pohbee_timer(pohbee)--; + + if (pohbee_timer(pohbee) <= 0) + { + // Move to next state + pohbee_mode(pohbee) = POHBEE_MODE_DESPAWN; + pohbee->fuse = 5*TICRATE; + } +} + +static void PohbeeDespawn(mobj_t *pohbee) +{ + pohbee->momz = 16 * pohbee->scale * P_MobjFlip(pohbee); +} + +static void DoGunSwing(mobj_t *gun, mobj_t *pohbee) +{ + const angle_t angle = gun->angle + ANGLE_90; + const tic_t swingTimer = leveltime + gun_offset(gun); + + const angle_t swingAmt = swingTimer * (ANGLE_MAX / GUN_SWINGTIME); + const fixed_t swingCos = FINECOSINE(swingAmt >> ANGLETOFINESHIFT); + + const angle_t pitch = -ANGLE_90 + FixedMul(swingCos, GUN_SWING); + const fixed_t dist = gun_numsegs(gun) * CHAIN_SIZE * gun->scale; + + fixed_t offsetX = FixedMul( + dist, FixedMul( + FINECOSINE(angle >> ANGLETOFINESHIFT), + FINECOSINE(pitch >> ANGLETOFINESHIFT) + ) + ); + + fixed_t offsetY = FixedMul( + dist, FixedMul( + FINESINE(angle >> ANGLETOFINESHIFT), + FINECOSINE(pitch >> ANGLETOFINESHIFT) + ) + ); + + fixed_t offsetZ = FixedMul( + dist, FINESINE(pitch >> ANGLETOFINESHIFT) + ); + + PohbeeMoveTo(gun, pohbee->x + offsetX, pohbee->y + offsetY, pohbee->z + offsetZ); +} + +static void ShrinkLaserThinker(mobj_t *pohbee, mobj_t *gun, mobj_t *laser) +{ + const fixed_t gunX = gun->x + gun->momx; + const fixed_t gunY = gun->y + gun->momy; + const fixed_t gunZ = P_GetMobjFeet(gun) + gun->momz; + + PohbeeMoveTo(laser, gunX, gunY, gun->floorz); + + if (ShrinkLaserActive(pohbee) == true) + { + mobj_t *particle = NULL; + + laser->renderflags &= ~RF_DONTDRAW; + laser->color = gun->color; + + if (leveltime & 1) + { + laser->spritexscale = 5*FRACUNIT/2; + } + else + { + laser->spritexscale = FRACUNIT; + } + + laser->spriteyscale = FixedDiv(FixedDiv(gunZ - gun->floorz, mapobjectscale), laser->info->height); + + particle = P_SpawnMobjFromMobj( + laser, + P_RandomRange(-16, 16) * FRACUNIT, + P_RandomRange(-16, 16) * FRACUNIT, + 0, + MT_SHRINK_PARTICLE + ); + + P_SetTarget(&gun_pohbee(particle), pohbee); + + particle->color = laser->color; + + P_SetScale(particle, particle->scale * 2); + particle->destscale = 0; + + //particle->momz = 2 * particle->scale * P_MobjFlip(particle); + } + else + { + laser->renderflags |= RF_DONTDRAW; + } +} + +static void DoGunChains(mobj_t *gun, mobj_t *pohbee) +{ + const fixed_t gunX = gun->x + gun->momx; + const fixed_t gunY = gun->y + gun->momy; + const fixed_t gunZ = P_GetMobjHead(gun) + gun->momz; + + const fixed_t beeX = pohbee->x + pohbee->momx; + const fixed_t beeY = pohbee->y + pohbee->momy; + const fixed_t beeZ = P_GetMobjFeet(pohbee) + pohbee->momz; + + const fixed_t offsetX = (beeX - gunX) / gun_numsegs(gun); + const fixed_t offsetY = (beeY - gunY) / gun_numsegs(gun); + const fixed_t offsetZ = (beeZ - gunZ) / gun_numsegs(gun); + + mobj_t *chain = NULL; + + fixed_t curX = gunX + (offsetX / 2); + fixed_t curY = gunY + (offsetY / 2); + fixed_t curZ = gunZ + (offsetZ / 2); + + chain = gun_chains(gun); + while (chain != NULL && P_MobjWasRemoved(chain) == false) + { + PohbeeMoveTo(chain, curX, curY, curZ); + + curX += offsetX; + curY += offsetY; + curZ += offsetZ; + + chain = gun_chains(chain); + } +} + +static void ShrinkGunThinker(mobj_t *gun) +{ + mobj_t *pohbee = gun_pohbee(gun); + + if (pohbee == NULL || P_MobjWasRemoved(pohbee) == true) + { + P_RemoveMobj(gun); + return; + } + + gun->angle = pohbee->angle; + gun->color = ShrinkLaserColor(pohbee); + + DoGunSwing(gun, pohbee); + + if (gun_laser(gun) != NULL && P_MobjWasRemoved(gun_laser(gun)) == false) + { + ShrinkLaserThinker(pohbee, gun, gun_laser(gun)); + } + + DoGunChains(gun, pohbee); +} + +void Obj_PohbeeThinker(mobj_t *pohbee) +{ + mobj_t *gun = NULL; + + pohbee->momx = pohbee->momy = pohbee->momz = 0; + + switch (pohbee_mode(pohbee)) + { + case POHBEE_MODE_SPAWN: + PohbeeSpawn(pohbee); + break; + + case POHBEE_MODE_ACT: + PohbeeAct(pohbee); + break; + + case POHBEE_MODE_DESPAWN: + PohbeeDespawn(pohbee); + break; + + default: + // failsafe + pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; + break; + } + + gun = pohbee_guns(pohbee); + while (gun != NULL && P_MobjWasRemoved(gun) == false) + { + ShrinkGunThinker(gun); + gun = pohbee_guns(gun); + } +} + +void Obj_PohbeeRemoved(mobj_t *pohbee) +{ + mobj_t *gun = pohbee_guns(pohbee); + + while (gun != NULL && P_MobjWasRemoved(gun) == false) + { + mobj_t *nextGun = pohbee_guns(gun); + P_RemoveMobj(gun); + gun = nextGun; + } +} + +void Obj_ShrinkGunRemoved(mobj_t *gun) +{ + mobj_t *chain = NULL; + + if (gun_laser(gun) != NULL && P_MobjWasRemoved(gun_laser(gun)) == false) + { + P_RemoveMobj(gun_laser(gun)); + } + + chain = gun_chains(gun); + while (chain != NULL && P_MobjWasRemoved(chain) == false) + { + mobj_t *nextChain = gun_chains(chain); + P_RemoveMobj(chain); + chain = nextChain; + } +} + +boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim) +{ + mobj_t *pohbee = gun_pohbee(gun); + mobj_t *owner = NULL; + INT32 prevTimer = 0; + + if (pohbee == NULL || P_MobjWasRemoved(pohbee) == true) + { + return true; + } + + if (ShrinkLaserActive(pohbee) == false) + { + return true; + } + + if (victim->player->shrinkLaserDelay > 0) + { + victim->player->shrinkLaserDelay = TICRATE; + return true; + } + + victim->player->shrinkLaserDelay = TICRATE; + + owner = pohbee_owner(pohbee); + prevTimer = victim->player->growshrinktimer; + + if (owner != NULL && victim == owner) + { + // Belongs to us. Give us Grow! + if (prevTimer < 0) + { + // Take away Shrink. + K_RemoveGrowShrink(victim->player); + } + else + { + victim->player->growshrinktimer += 3*TICRATE; + S_StartSound(victim, sfx_kc5a); + + if (prevTimer <= 0) + { + victim->scalespeed = mapobjectscale/TICRATE; + victim->destscale = FixedMul(mapobjectscale, GROW_SCALE); + + if (K_PlayerShrinkCheat(victim->player) == true) + { + victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE); + } + + if (victim->player->invincibilitytimer > 0) + { + ; // invincibility has priority in P_RestoreMusic, no point in starting here + } + else if (P_IsLocalPlayer(victim->player) == true) + { + S_ChangeMusicSpecial("kgrow"); + } + else //used to be "if (P_IsDisplayPlayer(victim->player) == false)" + { + S_StartSound(victim, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); + } + + P_RestoreMusic(victim->player); + } + } + } + else + { + if (prevTimer > 0) + { + // Take away Grow. + K_RemoveGrowShrink(victim->player); + } + else + { + // Start shrinking! + victim->player->growshrinktimer -= 5*TICRATE; + S_StartSound(victim, sfx_kc59); + + if (prevTimer >= 0) + { + //K_DropItems(victim->player); + + victim->scalespeed = mapobjectscale/TICRATE; + victim->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); + + if (K_PlayerShrinkCheat(victim->player) == true) + { + victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE); + } + } + } + } + + return true; +} + +static waypoint_t *GetPohbeeWaypoint(waypoint_t *anchor, const UINT32 traveldist, const boolean huntbackwards) +{ + const boolean useshortcuts = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + waypoint_t *ret = NULL; + + pathfindsuccess = K_PathfindThruCircuitSpawnable( + anchor, traveldist, + &pathtofinish, + useshortcuts, huntbackwards + ); + + if (pathfindsuccess == true) + { + ret = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata; + Z_Free(pathtofinish.array); + } + else + { + ret = anchor; + } + + return ret; + +} + +static waypoint_t *GetPohbeeStart(waypoint_t *anchor) +{ + const UINT32 traveldist = FixedMul(POHBEE_DIST >> 1, mapobjectscale) / FRACUNIT; + const boolean huntbackwards = true; + + return GetPohbeeWaypoint(anchor, traveldist, huntbackwards); +} + +static waypoint_t *GetPohbeeEnd(waypoint_t *anchor) +{ + const UINT32 traveldist = FixedMul(POHBEE_DIST, mapobjectscale) / FRACUNIT; + const boolean huntbackwards = false; + + return GetPohbeeWaypoint(anchor, traveldist, huntbackwards); +} + +static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UINT8 numLasers) +{ + mobj_t *pohbee = NULL; + + fixed_t size = 0; + INT32 baseSegs = INT32_MAX; + INT32 segVal = INT32_MAX; + mobj_t *prevGun = NULL; + + size_t i, j; + + if (owner == NULL || owner->mo == NULL || P_MobjWasRemoved(owner->mo) == true + || start == NULL || end == NULL + || numLasers == 0) + { + // Invalid inputs + return; + } + + // Calculate number of chain segments added per laser. + size = FixedMul(end->mobj->radius, 3*FRACUNIT/2); + segVal = max(1, 1 + ((size / start->mobj->scale) / CHAIN_SIZE) / numLasers); + baseSegs = segVal * numLasers; + + // Valid spawning conditions, + // we can start creating each individual part. + pohbee = P_SpawnMobjFromMobj(start->mobj, 0, 0, (baseSegs * CHAIN_SIZE * FRACUNIT) + POHBEE_HOVER * 3, MT_SHRINK_POHBEE); + P_SetTarget(&pohbee_owner(pohbee), owner->mo); + + pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; + pohbee_timer(pohbee) = POHBEE_TIME; + pohbee_height(pohbee) = size; + + pohbee_waypoint_cur(pohbee) = (INT32)K_GetWaypointHeapIndex(start); + pohbee_waypoint_dest(pohbee) = (INT32)K_GetWaypointHeapIndex(end); + + prevGun = pohbee; + + for (i = 0; i < numLasers; i++) + { + const UINT8 numSegs = segVal * (i + 1); + + mobj_t *gun = P_SpawnMobjFromMobj(pohbee, 0, 0, 0, MT_SHRINK_GUN); + mobj_t *laser = NULL; + mobj_t *prevChain = NULL; + + P_SetTarget(&gun_pohbee(gun), pohbee); + P_SetTarget(&pohbee_guns(prevGun), gun); + + gun_numsegs(gun) = numSegs; + gun_offset(gun) = P_RandomKey(GUN_SWINGTIME); + + laser = P_SpawnMobjFromMobj(gun, 0, 0, 0, MT_SHRINK_LASER); + P_SetTarget(&gun_laser(gun), laser); + + prevChain = gun; + for (j = 0; j < numSegs; j++) + { + mobj_t *chain = P_SpawnMobjFromMobj(gun, 0, 0, 0, MT_SHRINK_CHAIN); + + P_SetTarget(&gun_chains(prevChain), chain); + chain_index(chain) = j; + + prevChain = chain; + } + + prevGun = gun; + } +} + +void Obj_CreateShrinkPohbees(player_t *owner) +{ + UINT8 ownerPos = 1; + + struct { + waypoint_t *start; + waypoint_t *end; + UINT8 lasers; + boolean first; + } pohbees[MAXPLAYERS]; + size_t numPohbees = 0; + + size_t i, j; + + if (owner == NULL || owner->mo == NULL || P_MobjWasRemoved(owner->mo) == true) + { + return; + } + + ownerPos = owner->position; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *player = NULL; + waypoint_t *endWaypoint = 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; + } + + if (player->nextwaypoint == NULL) + { + // No waypoint? + continue; + } + + endWaypoint = GetPohbeeEnd(player->nextwaypoint); + + for (j = 0; j < numPohbees; j++) + { + if (pohbees[j].end == endWaypoint) + { + // Increment laser count for the already existing poh-bee, + // if another one would occupy the same space. + pohbees[j].lasers++; + break; + } + } + + if (j == numPohbees) + { + // Push a new poh-bee + pohbees[j].start = GetPohbeeStart(player->nextwaypoint); + pohbees[j].end = endWaypoint; + pohbees[j].lasers = 1; + + if (player->position == 1) + { + pohbees[j].first = true; + } + + numPohbees++; + } + } + + for (i = 0; i < numPohbees; i++) + { + CreatePohbee(owner, pohbees[i].start, pohbees[i].end, pohbees[i].lasers); + + if (pohbees[i].first == true) + { + // Add a chain of extra ones for 1st place. + waypoint_t *prev = pohbees[i].end; + + for (j = 0; j < EXTRA_FOR_FIRST; j++) + { + waypoint_t *new = GetPohbeeEnd(prev); + CreatePohbee(owner, prev, new, 1); + prev = new; + } + } + } +} diff --git a/src/p_map.c b/src/p_map.c index ad970ee45..e00cbea1c 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -32,6 +32,7 @@ #include "hu_stuff.h" // SRB2kart #include "i_system.h" // SRB2kart #include "k_terrain.h" +#include "k_objects.h" #include "r_splats.h" @@ -739,6 +740,81 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) // SRB2kart 011617 - Colission[sic] code for kart items //{ + if (thing->type == MT_SHRINK_GUN || thing->type == MT_SHRINK_PARTICLE) + { + if (tmthing->type != MT_PLAYER) + { + return BMIT_CONTINUE; + } + + if (thing->type == MT_SHRINK_GUN) + { + // Use special collision for the laser gun. + // The laser sprite itself is just a visual, + // the gun itself does the colliding for us. + if (tmthing->z > thing->z) + { + return BMIT_CONTINUE; // overhead + } + + if (tmthing->z + tmthing->height < thing->floorz) + { + return BMIT_CONTINUE; // underneath + } + } + else + { + if (tmthing->z > thing->z + thing->height) + { + return BMIT_CONTINUE; // overhead + } + + if (tmthing->z + tmthing->height < thing->z) + { + return BMIT_CONTINUE; // underneath + } + } + + return Obj_ShrinkLaserCollide(thing, tmthing) ? BMIT_CONTINUE : BMIT_ABORT; + } + else if (tmthing->type == MT_SHRINK_GUN || tmthing->type == MT_SHRINK_PARTICLE) + { + if (thing->type != MT_PLAYER) + { + return BMIT_CONTINUE; + } + + if (tmthing->type == MT_SHRINK_GUN) + { + // Use special collision for the laser gun. + // The laser sprite itself is just a visual, + // the gun itself does the colliding for us. + if (thing->z > tmthing->z) + { + return BMIT_CONTINUE; // overhead + } + + if (thing->z + thing->height < tmthing->floorz) + { + return BMIT_CONTINUE; // underneath + } + } + else + { + if (tmthing->z > thing->z + thing->height) + { + return BMIT_CONTINUE; // overhead + } + + if (tmthing->z + tmthing->height < thing->z) + { + return BMIT_CONTINUE; // underneath + } + } + + return Obj_ShrinkLaserCollide(tmthing, thing) ? BMIT_CONTINUE : BMIT_ABORT; + } + if (tmthing->type == MT_SMK_ICEBLOCK) { // see if it went over / under diff --git a/src/p_mobj.c b/src/p_mobj.c index b541744b3..0505d6bda 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7840,6 +7840,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) { @@ -10651,6 +10656,16 @@ void P_RemoveMobj(mobj_t *mobj) P_SetTarget(&mobj->player->followmobj, NULL); } + if (mobj->type == MT_SHRINK_POHBEE) + { + Obj_PohbeeRemoved(mobj); + } + + if (mobj->type == MT_SHRINK_GUN) + { + Obj_ShrinkGunRemoved(mobj); + } + mobj->health = 0; // Just because // unlink from sector and block lists diff --git a/src/p_saveg.c b/src/p_saveg.c index be02b13c6..5620178e1 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -367,6 +367,8 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].stairjank); + WRITEUINT8(save_p, players[i].shrinkLaserDelay); + // respawnvars_t WRITEUINT8(save_p, players[i].respawn.state); WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].respawn.wp)); @@ -654,6 +656,8 @@ static void P_NetUnArchivePlayers(void) players[i].stairjank = READUINT8(save_p); + players[i].shrinkLaserDelay = READUINT8(save_p); + // respawnvars_t players[i].respawn.state = READUINT8(save_p); players[i].respawn.wp = (waypoint_t *)(size_t)READUINT32(save_p); diff --git a/src/p_user.c b/src/p_user.c index 78d20a794..299d42a4b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3044,6 +3044,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall subsector_t *newsubsec; #endif + fixed_t playerScale = FixedDiv(player->mo->scale, mapobjectscale); + fixed_t scaleDiff = playerScale - FRACUNIT; + fixed_t cameraScale = mapobjectscale; + thiscam->old_x = thiscam->x; thiscam->old_y = thiscam->y; thiscam->old_z = thiscam->z; @@ -3132,8 +3136,11 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall return true; } - thiscam->radius = 20*mapobjectscale; - thiscam->height = 16*mapobjectscale; + // Adjust camera to match Grow/Shrink + cameraScale = FixedMul(cameraScale, FRACUNIT + (scaleDiff / 3)); + + thiscam->radius = 20*cameraScale; + thiscam->height = 16*cameraScale; // Don't run while respawning from a starpost // Inu 4/8/13 Why not?! @@ -3159,8 +3166,8 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camspeed = cv_cam_speed[num].value; camstill = cv_cam_still[num].value; camrotate = cv_cam_rotate[num].value; - camdist = FixedMul(cv_cam_dist[num].value, mapobjectscale); - camheight = FixedMul(cv_cam_height[num].value, mapobjectscale); + camdist = FixedMul(cv_cam_dist[num].value, cameraScale); + camheight = FixedMul(cv_cam_height[num].value, cameraScale); if (timeover) { @@ -3171,8 +3178,8 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall { const INT32 introcam = (introtime - leveltime); camrotate += introcam*5; - camdist += (introcam * mapobjectscale)*3; - camheight += (introcam * mapobjectscale)*2; + camdist += (introcam * cameraScale)*3; + camheight += (introcam * cameraScale)*2; } else if (player->exiting) // SRB2Kart: Leave the camera behind while exiting, for dramatic effect! camstill = true; @@ -3236,7 +3243,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall // sets ideal cam pos { - const fixed_t speedthreshold = 48*mapobjectscale; + const fixed_t speedthreshold = 48*cameraScale; const fixed_t olddist = P_AproxDistance(mo->x - thiscam->x, mo->y - thiscam->y); fixed_t lag, distoffset; @@ -3541,7 +3548,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall // point viewed by the camera // this point is just 64 unit forward the player - dist = 64*mapobjectscale; + dist = 64*cameraScale; viewpointx = mo->x + FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist) + xpan; viewpointy = mo->y + FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist) + ypan;