From 8979dafd4cefeb2540863e53a3b95a37f53f11b9 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 17 Sep 2022 06:34:11 -0700 Subject: [PATCH] Replace shitty item box pop with flying debris and dust clouds Debris flies forward and outward from the player in the direction of momentum. Debris particles bounce once then disappear when they hit the ground for the second time. Clouds spawn on and trail behind the player for a short duration. --- src/deh_tables.c | 9 ++ src/info.c | 61 +++++++++++ src/info.h | 11 ++ src/k_collide.c | 4 +- src/k_objects.h | 6 ++ src/objects/Sourcefile | 1 + src/objects/item-debris.c | 207 ++++++++++++++++++++++++++++++++++++++ src/p_enemy.c | 131 +++++++++++++++--------- src/p_mobj.c | 17 ++++ 9 files changed, 396 insertions(+), 51 deletions(-) create mode 100644 src/objects/item-debris.c diff --git a/src/deh_tables.c b/src/deh_tables.c index 47a6ca515..12911c27a 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -337,6 +337,7 @@ actionpointer_t actionpointers[] = {{A_ReaperThinker}, "A_REAPERTHINKER"}, {{A_FlameShieldPaper}, "A_FLAMESHIELDPAPER"}, {{A_InvincSparkleRotate}, "A_INVINCSPARKLEROTATE"}, + {{A_SpawnItemDebrisCloud}, "A_SPAWNITEMDEBRISCLOUD"}, {{NULL}, "NONE"}, @@ -3277,6 +3278,12 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_RANDOMITEMPOP4", //} + "S_ITEM_DEBRIS", + "S_ITEM_DEBRIS_CLOUD_SPAWNER_INIT", + "S_ITEM_DEBRIS_CLOUD_SPAWNER1", + "S_ITEM_DEBRIS_CLOUD_SPAWNER2", + "S_ITEM_DEBRIS_CLOUD_SPAWNER3", + "S_ITEMICON", // Item capsules @@ -5287,6 +5294,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BRAKEDRIFT", "MT_BRAKEDUST", "MT_DRIFTDUST", + "MT_ITEM_DEBRIS", + "MT_ITEM_DEBRIS_CLOUD_SPAWNER", "MT_DRIFTELECTRICITY", "MT_DRIFTELECTRICSPARK", "MT_JANKSPARK", diff --git a/src/info.c b/src/info.c index f8d573ab4..9f9addcd3 100644 --- a/src/info.c +++ b/src/info.c @@ -530,6 +530,7 @@ char sprnames[NUMSPRITES + 1][5] = "RNDM", // Random Item Box "SBOX", // Sphere Box (for Battle) "RPOP", // Random Item Box Pop + "ITRI", // Item Box Debris "SGNS", // Signpost sparkle "FAST", // Speed boost trail "DSHR", // Speed boost dust release @@ -3864,6 +3865,12 @@ state_t states[NUMSTATES] = {SPR_RPOP, FF_FULLBRIGHT|2, 5, {NULL}, 0, 0, S_RANDOMITEMPOP4}, // S_RANDOMITEMPOP3 {SPR_RPOP, FF_FULLBRIGHT|3, 5, {NULL}, 0, 0, S_NULL}, // S_RANDOMITEMPOP4 + {SPR_ITRI, FF_FULLBRIGHT, -1, {NULL}, 19, 1, S_NULL}, // S_ITEM_DEBRIS + {SPR_NULL, 0, 1, {NULL}, 0, 0, S_ITEM_DEBRIS_CLOUD_SPAWNER1}, // S_ITEM_DEBRIS_CLOUD_SPAWNER_INIT + {SPR_NULL, 0, 1, {A_Repeat}, 4, S_ITEM_DEBRIS_CLOUD_SPAWNER2, S_NULL}, // S_ITEM_DEBRIS_CLOUD_SPAWNER1 + {SPR_NULL, 4, 2, {A_SpawnItemDebrisCloud}, 1, 10, S_ITEM_DEBRIS_CLOUD_SPAWNER3}, // S_ITEM_DEBRIS_CLOUD_SPAWNER2 + {SPR_NULL, 4, 5, {A_SpawnItemDebrisCloud}, 0, 2, S_ITEM_DEBRIS_CLOUD_SPAWNER1}, // S_ITEM_DEBRIS_CLOUD_SPAWNER3 + {SPR_NULL, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMICON {SPR_ICAP, FF_ADD|0, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMCAPSULE @@ -23091,6 +23098,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_ITEM_DEBRIS + -1, // doomednum + S_ITEM_DEBRIS, // spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // 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 + 32*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_ITEM_DEBRIS_CLOUD_SPAWNER + -1, // doomednum + S_ITEM_DEBRIS_CLOUD_SPAWNER_INIT, // spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // 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 + 32*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOSECTOR|MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + { // MT_DRIFTELECTRICITY -1, // doomednum S_DRIFTELECTRICITY, // spawnstate diff --git a/src/info.h b/src/info.h index ab1867973..2f0bc535f 100644 --- a/src/info.h +++ b/src/info.h @@ -290,6 +290,7 @@ enum actionnum A_REAPERTHINKER, A_FLAMESHIELDPAPER, A_INVINCSPARKLEROTATE, + A_SPAWNITEMDEBRISCLOUD, NUMACTIONS }; @@ -563,6 +564,7 @@ void A_ReaperThinker(); void A_MementosTPParticles(); void A_FlameShieldPaper(); void A_InvincSparkleRotate(); +void A_SpawnItemDebrisCloud(); extern boolean actionsoverridden[NUMACTIONS]; @@ -1076,6 +1078,7 @@ typedef enum sprite SPR_RNDM, // Random Item Box SPR_SBOX, // Sphere Box (for Battle) SPR_RPOP, // Random Item Box Pop + SPR_ITRI, // Item Box Debris SPR_SGNS, // Signpost sparkle SPR_FAST, // Speed boost trail SPR_DSHR, // Speed boost dust release @@ -4267,6 +4270,12 @@ typedef enum state S_RANDOMITEMPOP4, //} + S_ITEM_DEBRIS, + S_ITEM_DEBRIS_CLOUD_SPAWNER_INIT, + S_ITEM_DEBRIS_CLOUD_SPAWNER1, + S_ITEM_DEBRIS_CLOUD_SPAWNER2, + S_ITEM_DEBRIS_CLOUD_SPAWNER3, + S_ITEMICON, // Item capsules @@ -6313,6 +6322,8 @@ typedef enum mobj_type MT_BRAKEDRIFT, MT_BRAKEDUST, MT_DRIFTDUST, + MT_ITEM_DEBRIS, + MT_ITEM_DEBRIS_CLOUD_SPAWNER, MT_DRIFTELECTRICITY, MT_DRIFTELECTRICSPARK, MT_JANKSPARK, diff --git a/src/k_collide.c b/src/k_collide.c index 128364cfc..0658edb5f 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -11,6 +11,7 @@ #include "hu_stuff.h" // Sink snipe print #include "doomdef.h" // Sink snipe print #include "g_game.h" // Sink snipe print +#include "k_objects.h" angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2) { @@ -265,8 +266,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) } else { - mobj_t *poof = P_SpawnMobj(t1->x, t1->y, t1->z, MT_EXPLODE); - S_StartSound(poof, t1->info->deathsound); + Obj_SpawnItemDebrisEffects(t1, t2); #if 0 // Eggbox snipe! diff --git a/src/k_objects.h b/src/k_objects.h index 45f9df78c..e32801942 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -8,4 +8,10 @@ void Obj_HyudoroThink(mobj_t *actor); void Obj_HyudoroCenterThink(mobj_t *actor); void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher); +/* Item Debris */ +fixed_t Obj_GetItemDebrisSpeed(mobj_t *collector, fixed_t min_speed); +void Obj_SpawnItemDebrisEffects(mobj_t *collectible, mobj_t *collector); +void Obj_ItemDebrisThink(mobj_t *debris); +fixed_t Obj_ItemDebrisBounce(mobj_t *debris, fixed_t momz); + #endif/*k_objects_H*/ diff --git a/src/objects/Sourcefile b/src/objects/Sourcefile index f7e4f2491..66480688f 100644 --- a/src/objects/Sourcefile +++ b/src/objects/Sourcefile @@ -1 +1,2 @@ hyudoro.c +item-debris.c diff --git a/src/objects/item-debris.c b/src/objects/item-debris.c new file mode 100644 index 000000000..dcb1950d7 --- /dev/null +++ b/src/objects/item-debris.c @@ -0,0 +1,207 @@ +#include "../doomdef.h" +#include "../d_player.h" +#include "../m_random.h" +#include "../k_kart.h" +#include "../k_objects.h" +#include "../p_local.h" +#include "../s_sound.h" + +// TODO: general function +static fixed_t K_GetPlayerSpeedRatio(player_t *player) +{ + return FixedDiv(player->speed, + K_GetKartSpeed(player, false, false)); +} + +#define debris_type(o) ((o)->extravalue1) +#define debris_bouncesleft(o) ((o)->threshold) + +enum { + DEBRIS_ALPHA, + DEBRIS_BETA, + + NUM_DEBRIS_TYPES +}; + +struct debris_config { + mobj_t * origin; + angle_t angle; + fixed_t speed; + fixed_t scale; + UINT8 type; +}; + +static fixed_t +get_speed_ratio (mobj_t *thing) +{ + return thing->player ? + K_GetPlayerSpeedRatio(thing->player) : FRACUNIT; +} + +static void +spawn_debris +( const struct debris_config * config, + INT32 angle) +{ + const fixed_t height_table[NUM_DEBRIS_TYPES] = { + 50*FRACUNIT, + 35*FRACUNIT, + }; + + mobj_t *debris = P_SpawnMobjFromMobj( + config->origin, 0, 0, 0, MT_ITEM_DEBRIS); + + const state_t *st = debris->state; + + debris_type(debris) = config->type; + debris_bouncesleft(debris) = 1; + + // Start at a random frame of animation + debris->frame = (debris->frame & ~(FF_FRAMEMASK)) | + P_RandomRange((st->frame & FF_FRAMEMASK), st->var1); + + P_InstaThrust(debris, + config->angle + angle, + config->speed); + + P_SetObjectMomZ(debris, + FixedMul(config->scale, + height_table[config->type]), + false); + + debris->destscale = + FixedMul(config->scale, 3 * debris->scale); + P_SetScale(debris, debris->destscale); + + // Pass down color to dust particles + debris->color = config->origin->color; +} + +static void +spawn_cloud +( mobj_t * collectible, + mobj_t * collector) +{ + mobj_t *spawner = P_SpawnMobjFromMobj(collectible, + 0, 0, 0, MT_ITEM_DEBRIS_CLOUD_SPAWNER); + + P_SetTarget(&spawner->target, collector); + + S_StartSound(spawner, sfx_kc2e); + S_StartSound(spawner, sfx_s1c9); +} + +static void +rotate3d (mobj_t *debris) +{ + const UINT8 steps = 30; + + debris->rollangle = + M_RandomKey(steps) * (ANGLE_MAX / steps); +} + +fixed_t +Obj_GetItemDebrisSpeed +( mobj_t * collector, + fixed_t min_speed) +{ + const fixed_t base_speed = FixedMul( + 75 * mapobjectscale, + get_speed_ratio(collector)); + + return max(base_speed, min_speed); +} + +void +Obj_SpawnItemDebrisEffects +( mobj_t * collectible, + mobj_t * collector) +{ + const fixed_t min_speed = 80 * collectible->scale; + + const fixed_t speed = + Obj_GetItemDebrisSpeed(collector, min_speed); + + struct debris_config config = { + .origin = collectible, + .angle = K_MomentumAngle(collector), + .speed = speed, + .scale = FixedDiv(speed, min_speed), + }; + + config.type = DEBRIS_ALPHA; + + spawn_debris(&config, ANGLE_11hh); + spawn_debris(&config, -(ANGLE_11hh)); + + config.type = DEBRIS_BETA; + + spawn_debris(&config, 3*ANGLE_22h/2); + spawn_debris(&config, 3*ANGLE_22h/4); + spawn_debris(&config, 0); + spawn_debris(&config, -(3*ANGLE_22h/4)); + spawn_debris(&config, -(3*ANGLE_22h/2)); + + spawn_cloud(collectible, collector); +} + +void +Obj_ItemDebrisThink (mobj_t *debris) +{ + const UINT8 frame = (debris->frame & FF_FRAMEMASK); + + if (debris->momz == 0) + { + P_KillMobj(debris, NULL, NULL, DMG_NORMAL); + return; + } + + rotate3d(debris); + + if (frame % 3 == 1) + { + mobj_t *ghost = P_SpawnGhostMobj(debris); + + ghost->fuse = 3; + } + + if (debris_type(debris) == DEBRIS_ALPHA) + { + mobj_t *dust = P_SpawnMobjFromMobj( + debris, 0, 0, 0, MT_SPINDASHDUST); + + P_SetScale(dust, (dust->destscale /= 3)); + + dust->color = debris->color; + dust->colorized = true; + + dust->momx = debris->momx / 4; + dust->momy = debris->momy / 4; + dust->momz = debris->momz / 4; + } +} + +fixed_t +Obj_ItemDebrisBounce +( mobj_t * debris, + fixed_t momz) +{ + if (debris_bouncesleft(debris) <= 0) + { + P_KillMobj(debris, NULL, NULL, DMG_NORMAL); + return 0; + } + + momz = -(momz); + + if (debris_type(debris) == DEBRIS_BETA) + { + momz /= 2; + } + + debris_bouncesleft(debris)--; + + S_StartSound(debris, sfx_cdfm47); + + return momz; +} diff --git a/src/p_enemy.c b/src/p_enemy.c index 2c69e7bfe..37bd90a0f 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -32,6 +32,7 @@ #include "k_battle.h" #include "k_respawn.h" #include "k_collide.h" +#include "k_objects.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -326,6 +327,7 @@ void A_ReaperThinker(mobj_t *actor); void A_MementosTPParticles(mobj_t *actor); void A_FlameShieldPaper(mobj_t *actor); void A_InvincSparkleRotate(mobj_t *actor); +void A_SpawnItemDebrisCloud(mobj_t *actor); //for p_enemy.c @@ -13176,9 +13178,6 @@ void A_ItemPop(mobj_t *actor) { INT32 locvar1 = var1; - mobj_t *remains; - mobjtype_t explode; - if (LUA_CallAction(A_ITEMPOP, actor)) return; @@ -13195,58 +13194,13 @@ void A_ItemPop(mobj_t *actor) actor->flags |= MF_NOCLIP; P_SetThingPosition(actor); - // item explosion - explode = mobjinfo[actor->info->damage].mass; - remains = P_SpawnMobj(actor->x, actor->y, - ((actor->eflags & MFE_VERTICALFLIP) ? (actor->z + 3*(actor->height/4) - FixedMul(mobjinfo[explode].height, actor->scale)) : (actor->z + actor->height/4)), explode); - if (actor->eflags & MFE_VERTICALFLIP) - { - remains->eflags |= MFE_VERTICALFLIP; - remains->flags2 |= MF2_OBJECTFLIP; - } - remains->destscale = actor->destscale; - P_SetScale(remains, actor->scale); - - remains = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->damage); - remains->type = actor->type; // Transfer type information - P_UnsetThingPosition(remains); - if (sector_list) - { - P_DelSeclist(sector_list); - sector_list = NULL; - } - P_SetThingPosition(remains); - remains->destscale = actor->destscale; - P_SetScale(remains, actor->scale); - remains->flags = actor->flags; // Transfer flags - remains->flags2 = actor->flags2; // Transfer flags2 - remains->fuse = actor->fuse; // Transfer respawn timer - remains->cvmem = leveltime; - remains->threshold = actor->threshold; - if (remains->threshold != 69 && remains->threshold != 70) - { - remains->threshold = 68; - } - // To insure this information doesn't have to be rediscovered every time you look at this function... - // A threshold of 0 is for a "living", ordinary random item. - // 68 means regular popped random item debris. - // 69 used to mean old Karma Item behaviour (now you can replicate this with MF2_DONTRESPAWN). - // 70 is a powered up Overtime item. - remains->skin = NULL; - remains->spawnpoint = actor->spawnpoint; - - P_SetTarget(&tmthing, remains); - - if (actor->info->deathsound) - S_StartSound(remains, actor->info->deathsound); + Obj_SpawnItemDebrisEffects(actor, actor->target); if (locvar1 == 1) P_GivePlayerSpheres(actor->target->player, actor->extravalue1); else if (locvar1 == 0) actor->target->player->itemroulette = 1; - remains->flags2 &= ~MF2_AMBUSH; - // Here at mapload in battle? if ((gametyperules & GTR_BUMPERS) && (actor->flags2 & MF2_BOSSNOTRAP)) numgotboxes++; @@ -14535,3 +14489,82 @@ void A_InvincSparkleRotate(mobj_t *actor) ghost->fuse = 4; } } + +// Function: A_SpawnItemDebrisCloud +// +// Description: Spawns a particle effect relative to the location of the actor +// +// var1 = If 1, scale size and momentum by extravalue2 / frame. +// var2 = Number of particles to spawn. +// +void +A_SpawnItemDebrisCloud (mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + + const fixed_t min_speed = 90 * actor->scale; + const INT16 spacing = (actor->radius / 2) / actor->scale; + + fixed_t fade = FRACUNIT; + fixed_t scale_fade = FRACUNIT; + + mobj_t *target = actor->target; + + fixed_t speed; + fixed_t scale; + + INT32 i; + + if (target == NULL) + { + return; + } + + if (locvar1) + { + const UINT8 frame = (actor->frame & FF_FRAMEMASK); + fixed_t frac; + + if (frame == 0) + { + return; // div by zero + } + + // extravalue2 from A_Repeat + frac = fade / frame; + fade = actor->extravalue2 * frac; + scale_fade = fade + frac; + } + + speed = Obj_GetItemDebrisSpeed(target, min_speed); + scale = 2 * FixedMul(FixedDiv(speed, min_speed), scale_fade); + + // Most of this code is from p_inter.c, MT_ITEMCAPSULE + + // dust effects + for (i = 0; i < locvar2; i++) + { + mobj_t *puff = P_SpawnMobjFromMobj( + target, + P_RandomRange(-spacing, spacing) * FRACUNIT, + P_RandomRange(-spacing, spacing) * FRACUNIT, + P_RandomRange(0, 4 * spacing) * FRACUNIT, + MT_SPINDASHDUST + ); + + puff->color = target->color; + puff->colorized = true; + + puff->destscale = FixedMul(puff->destscale, scale); + P_SetScale(puff, puff->destscale); + + puff->momz = puff->scale * P_MobjFlip(puff); + + P_Thrust(puff, R_PointToAngle2(target->x, target->y, puff->x, puff->y), 3 * puff->scale); + + puff->momx += FixedMul(target->momx, fade); + puff->momy += FixedMul(target->momy, fade); + puff->momz += FixedMul(target->momz, fade); + } +} diff --git a/src/p_mobj.c b/src/p_mobj.c index ae035df04..4115cdeeb 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1209,6 +1209,9 @@ fixed_t P_GetMobjGravity(mobj_t *mo) case MT_KARMAFIREWORK: gravityadd /= 3; break; + case MT_ITEM_DEBRIS: + gravityadd *= 6; + break; default: break; } @@ -2339,6 +2342,15 @@ boolean P_ZMovement(mobj_t *mo) mom.z = P_MobjFlip(mo)*FixedMul(5*FRACUNIT, mo->scale); else if (mo->type == MT_SPINFIRE) // elemental shield fire is another exception here ; + else if (mo->type == MT_ITEM_DEBRIS) + { + mom.z = Obj_ItemDebrisBounce(mo, mom.z); + + if (mom.z == 0) + { + return false; + } + } else if (mo->type == MT_DRIFTCLIP) { mom.z = -mom.z/2; @@ -7886,6 +7898,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_HyudoroCenterThink(mobj); break; } + case MT_ITEM_DEBRIS: + { + Obj_ItemDebrisThink(mobj); + break; + } case MT_ROCKETSNEAKER: if (!mobj->target || !mobj->target->health) {