From 459c5615af3c991f1d4934cc8e889f37e51e2d46 Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 14 Jun 2021 19:04:55 +1000 Subject: [PATCH] - Added respawn functionality to item capsules - Restructured some item capsule code to reduce thinker density and allow the item capsule parts to think after the capsule - Super Ring capsules are now semibright --- src/deh_tables.c | 1 + src/info.c | 4 +- src/info.h | 2 + src/p_enemy.c | 136 +++++++++++++++++++++++++++++++++++++++ src/p_inter.c | 3 + src/p_local.h | 2 + src/p_mobj.c | 162 +++++++++++------------------------------------ 7 files changed, 182 insertions(+), 128 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 128f14726..a97d56865 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -334,6 +334,7 @@ actionpointer_t actionpointers[] = {{A_ReaperThinker}, "A_REAPERTHINKER"}, {{A_FlameShieldPaper}, "A_FLAMESHIELDPAPER"}, {{A_InvincSparkleRotate}, "A_INVINCSPARKLEROTATE"}, + {{A_SpawnItemCapsuleParts}, "A_SPAWNITEMCAPSULEPARTS"}, {{NULL}, "NONE"}, diff --git a/src/info.c b/src/info.c index 920de1d51..ef941c34b 100644 --- a/src/info.c +++ b/src/info.c @@ -4067,7 +4067,7 @@ state_t states[NUMSTATES] = {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 + {SPR_ICAP, FF_ADD|0, -1, {A_SpawnItemCapsuleParts}, 0, 0, S_NULL}, // S_ITEMCAPSULE {SPR_ICAP, FF_PAPERSPRITE|1, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMCAPSULE_TOP_SIDE {SPR_ICAP, FF_VERTICALFLIP|FF_PAPERSPRITE|1, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMCAPSULE_BOTTOM_SIDE_AIR {SPR_ICAP, FF_PAPERSPRITE|2, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMCAPSULE_BOTTOM_SIDE_GROUND @@ -23088,7 +23088,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_SLIDEME|MF_SPECIAL|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + MF_SLIDEME|MF_SPECIAL|MF_RUNSPAWNFUNC|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/info.h b/src/info.h index 7b3310701..ab9456cae 100644 --- a/src/info.h +++ b/src/info.h @@ -287,6 +287,7 @@ enum actionnum A_REAPERTHINKER, A_FLAMESHIELDPAPER, A_INVINCSPARKLEROTATE, + A_SPAWNITEMCAPSULEPARTS, NUMACTIONS }; @@ -557,6 +558,7 @@ void A_ReaperThinker(); void A_MementosTPParticles(); void A_FlameShieldPaper(); void A_InvincSparkleRotate(); +void A_SpawnItemCapsuleParts(); extern boolean actionsoverridden[NUMACTIONS]; diff --git a/src/p_enemy.c b/src/p_enemy.c index 9dbeb8f5b..b472e8259 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -321,6 +321,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_SpawnItemCapsuleParts(mobj_t *actor); //for p_enemy.c @@ -14651,3 +14652,138 @@ void A_InvincSparkleRotate(mobj_t *actor) actor->angle += ANG1*10*(actor->extravalue2); // Arbitrary value, change this if you want, I suppose. } + +void P_RefreshItemCapsuleParts(mobj_t *mobj) +{ + UINT8 numNumbers = 0; + INT32 count = 0; + INT32 itemType = mobj->threshold; + mobj_t *part = mobj->tracer; + skincolornum_t color; + + if (itemType < 1 || itemType >= NUMKARTITEMS) + itemType = KITEM_SAD; + + // update number frame + if (K_GetShieldFromItem(itemType) != KSHIELD_NONE) // shields don't stack, so don't show a number + ; + else + { + switch (itemType) + { + case KITEM_ORBINAUT: // only display the number when the sprite no longer changes + if (mobj->movecount - 1 > K_GetOrbinautItemFrame(mobj->movecount)) + count = mobj->movecount; + break; + case KITEM_SUPERRING: // always display the number, and multiply it by 5 + count = mobj->movecount * 5; + break; + case KITEM_SAD: // never display the number + break; + default: + if (mobj->movecount > 1) + count = mobj->movecount; + break; + } + } + + while (count > 0) + { + if (P_MobjWasRemoved(part->tracer)) + { + P_SetTarget(&part->tracer, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY)); + P_SetTarget(&part->tracer->target, part); + P_SetMobjState(part->tracer, S_INVISIBLE); + part->tracer->spriteyoffset = 10*FRACUNIT; + part->tracer->spritexoffset = 13*numNumbers*FRACUNIT; + } + part = part->tracer; + part->sprite = SPR_ITMN; + part->frame = FF_FULLBRIGHT|(count % 10); + count /= 10; + numNumbers++; + } + + // delete any extra overlays (I guess in case the number changes?) + if (part->tracer) + { + P_RemoveMobj(part->tracer); + P_SetTarget(&part->tracer, NULL); + } + + // update color + color = (itemType == KITEM_SUPERRING ? SKINCOLOR_GOLD : SKINCOLOR_NONE); + part = mobj; + while (!P_MobjWasRemoved(part->hnext)) + { + part = part->hnext; + part->color = color; + part->colorized = (color != SKINCOLOR_NONE); + if (part->colorized) + part->renderflags |= RF_SEMIBRIGHT; + else + part->renderflags &= ~RF_SEMIBRIGHT; + } +} + +#define CAPSULESIDES 5 +#define ANG_CAPSULE (UINT32_MAX / CAPSULESIDES) +#define ROTATIONSPEED (2*ANG2) +void A_SpawnItemCapsuleParts(mobj_t *actor) +{ + UINT8 i; + mobj_t *part; + fixed_t buttScale = 0; + statenum_t buttState = S_ITEMCAPSULE_BOTTOM_SIDE_AIR; + angle_t spin = -ROTATIONSPEED; + + if (LUA_CallAction(A_SPAWNITEMCAPSULEPARTS, actor)) + return; + + if (P_IsObjectOnGround(actor)) + { + buttScale = 13*FRACUNIT/10; + buttState = S_ITEMCAPSULE_BOTTOM_SIDE_GROUND; + spin = 0; + } + + // inside item + part = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_ITEMCAPSULE_PART); + P_SetTarget(&part->target, actor); + P_SetMobjState(part, S_ITEMICON); + part->movedir = ROTATIONSPEED; // rotation speed + part->extravalue1 = 175*FRACUNIT/100; // relative scale + part->flags2 |= MF2_CLASSICPUSH|MF2_INFLOAT; // classicpush = centered horizontally, infloat = don't recolor + P_SetTarget(&actor->tracer, part); // pointer to this item, so we can modify its sprite/frame + + // capsule caps + part = actor; + for (i = 0; i < CAPSULESIDES; i++) + { + // a bottom side + P_SetTarget(&part->hnext, P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_ITEMCAPSULE_PART)); + P_SetTarget(&part->hnext->hprev, part); + part = part->hnext; + P_SetTarget(&part->target, actor); + P_SetMobjState(part, buttState); + part->angle = i * ANG_CAPSULE; + part->movedir = spin; // rotation speed + part->movefactor = 0; // z offset + part->extravalue1 = buttScale; // relative scale + + // a top side + P_SetTarget(&part->hnext, P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_ITEMCAPSULE_PART)); + P_SetTarget(&part->hnext->hprev, part); + part = part->hnext; + P_SetTarget(&part->target, actor); + P_SetMobjState(part, S_ITEMCAPSULE_TOP_SIDE); + part->angle = i * ANG_CAPSULE; + part->movedir = spin; // rotation speed + part->movefactor = actor->info->height - part->info->height; // z offset + } + + P_RefreshItemCapsuleParts(actor); +} +#undef CAPSULESIDES +#undef ANG_CAPSULE +#undef ROTATIONSPEED diff --git a/src/p_inter.c b/src/p_inter.c index 861cf2f84..2f98dd178 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -284,6 +284,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) return; + if (special->scale != special->destscale) // don't break it while it's respawning + return; + S_StartSound(toucher, special->info->deathsound); P_KillMobj(special, toucher, toucher, DMG_NORMAL); return; diff --git a/src/p_local.h b/src/p_local.h index a143ba9ab..9c282499f 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -365,6 +365,8 @@ void P_InternalFlickyBubble(mobj_t *actor); void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez); void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle); +void P_RefreshItemCapsuleParts(mobj_t *mobj); + // // P_MAP // diff --git a/src/p_mobj.c b/src/p_mobj.c index e02376d9a..0255f775f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3765,20 +3765,12 @@ static void P_ItemCapsulePartThinker(mobj_t *mobj) mobj_t *target = mobj->target; fixed_t targetScale, z; - if (P_MobjWasRemoved(mobj->target)) // if the capsule was removed, remove the parts too + if (P_MobjWasRemoved(target)) { P_RemoveMobj(mobj); return; } - // ring capsules have a different color - if (!(mobj->flags2 & MF2_INFLOAT) - &&mobj->color != target->color) - { - mobj->color = target->color; - mobj->colorized = (mobj->color != SKINCOLOR_NONE); - } - // match the capsule's scale if (mobj->extravalue1) targetScale = FixedMul(mobj->extravalue1, target->scale); @@ -6149,17 +6141,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_ITEMCAPSULE: if (!P_MobjWasRemoved(mobj->tracer)) { - UINT8 numNumbers = 0; - INT32 count = 0; INT32 itemType = mobj->threshold; mobj_t *part = mobj->tracer; if (itemType < 1 || itemType >= NUMKARTITEMS) itemType = KITEM_SAD; - // update color - mobj->color = (itemType == KITEM_SUPERRING ? SKINCOLOR_GOLD : SKINCOLOR_NONE); - // update inside item frame switch (itemType) { @@ -6180,53 +6167,6 @@ static boolean P_MobjRegularThink(mobj_t *mobj) part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(itemType); break; } - - // update number frame - if (K_GetShieldFromItem(itemType) != KSHIELD_NONE) // shields don't stack, so don't show a number - ; - else - { - switch (itemType) - { - case KITEM_ORBINAUT: // only display the number when the sprite no longer changes - if (mobj->movecount - 1 > K_GetOrbinautItemFrame(mobj->movecount)) - count = mobj->movecount; - break; - case KITEM_SUPERRING: // always display the number, and multiply it by 5 - count = mobj->movecount * 5; - break; - case KITEM_SAD: // never display the number - break; - default: - if (mobj->movecount > 1) - count = mobj->movecount; - break; - } - } - - while (count > 0) - { - if (P_MobjWasRemoved(part->tracer)) - { - P_SetTarget(&part->tracer, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY)); - P_SetTarget(&part->tracer->target, part); - P_SetMobjState(part->tracer, S_INVISIBLE); - part->tracer->spriteyoffset = 10*FRACUNIT; - part->tracer->spritexoffset = 13*numNumbers*FRACUNIT; - } - part = part->tracer; - part->sprite = SPR_ITMN; - part->frame = FF_FULLBRIGHT|(count % 10); - count /= 10; - numNumbers++; - } - - // delete any extra overlays (I guess in case the number changes?) - if (part->tracer) - { - P_RemoveMobj(part->tracer); - P_SetTarget(&part->tracer, NULL); - } } break; case MT_ORBINAUT: @@ -9481,15 +9421,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // SRB2Kart case MT_ITEMCAPSULE: { -#define CAPSULESIDES 5 -#define ANG_CAPSULE (UINT32_MAX / CAPSULESIDES) -#define ROTATIONSPEED (2*ANG2) - UINT8 i; - mobj_t *part; - fixed_t buttScale = 0; - statenum_t buttState = S_ITEMCAPSULE_BOTTOM_SIDE_AIR; - angle_t spin = -ROTATIONSPEED; + fixed_t oldHeight = mobj->height; + // set default item & count #if 0 // set to 1 to test capsules with random items, e.g. with objectplace if (P_RandomChance(FRACUNIT/3)) mobj->threshold = KITEM_SUPERRING; @@ -9503,54 +9437,18 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->movecount = 1; #endif + // set starting scale + mobj->scalespeed >>= 1; + P_SetScale(mobj, mapobjectscale >> 4); + if (mobj->eflags & MFE_VERTICALFLIP) + mobj->z += (oldHeight - mobj->height); + + // grounded/aerial properties P_CheckPosition(mobj, mobj->x, mobj->y); // look for FOFs - if (P_IsObjectOnGround(mobj)) - { - buttScale = 13*FRACUNIT/10; - buttState = S_ITEMCAPSULE_BOTTOM_SIDE_GROUND; - spin = 0; - } - else + if (!P_IsObjectOnGround(mobj)) mobj->flags |= MF_NOGRAVITY; - // inside item - part = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_ITEMCAPSULE_PART); - P_SetTarget(&part->target, mobj); - P_SetMobjState(part, S_ITEMICON); - part->movedir = ROTATIONSPEED; // rotation speed - part->extravalue1 = 175*FRACUNIT/100; // relative scale - part->flags2 |= MF2_CLASSICPUSH|MF2_INFLOAT; // classicpush = centered horizontally, infloat = don't recolor - P_SetTarget(&mobj->tracer, part); // pointer to this item, so we can modify its sprite/frame - - // capsule caps - part = mobj; - for (i = 0; i < CAPSULESIDES; i++) - { - // a bottom side - P_SetTarget(&part->hnext, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_ITEMCAPSULE_PART)); - P_SetTarget(&part->hnext->hprev, part); - part = part->hnext; - P_SetTarget(&part->target, mobj); - P_SetMobjState(part, buttState); - part->angle = i * ANG_CAPSULE; - part->movedir = spin; // rotation speed - part->movefactor = 0; // z offset - part->extravalue1 = buttScale; // relative scale - - // a top side - P_SetTarget(&part->hnext, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_ITEMCAPSULE_PART)); - P_SetTarget(&part->hnext->hprev, part); - part = part->hnext; - P_SetTarget(&part->target, mobj); - P_SetMobjState(part, S_ITEMCAPSULE_TOP_SIDE); - part->angle = i * ANG_CAPSULE; - part->movedir = spin; // rotation speed - part->movefactor = mobj->info->height - part->info->height; // z offset - } break; -#undef CAPSULESIDES -#undef ANG_CAPSULE -#undef ROTATIONSPEED } case MT_KARMAHITBOX: { @@ -9876,7 +9774,8 @@ void P_RemoveMobj(mobj_t *mobj) // Rings only, please! if (mobj->spawnpoint && (mobj->type == MT_RING - || mobj->type == MT_BLUESPHERE) + || mobj->type == MT_BLUESPHERE + || mobj->type == MT_ITEMCAPSULE) && !(mobj->flags2 & MF2_DONTRESPAWN)) { itemrespawnque[iquehead] = mobj->spawnpoint; @@ -10284,6 +10183,17 @@ void P_RespawnSpecials(void) //if (!(gametyperules & GTR_CIRCUIT) && numgotboxes >= (4*nummapboxes/5)) // Battle Mode respawns all boxes in a different way //P_RespawnBattleBoxes(); + // nothing left to respawn? + if (iquehead == iquetail) + return; + + mthing = itemrespawnque[iquetail]; + +#ifdef PARANOIA + if (!mthing) + I_Error("itemrespawnque[iquetail] is NULL!"); +#endif + // wait time depends on player count for (p = 0; p < MAXPLAYERS; p++) { @@ -10291,6 +10201,11 @@ void P_RespawnSpecials(void) pcount++; } +#if 0 // set to 1 to enable quick respawns for testing + if (true) + time = 5*TICRATE; + else +#endif if (gametyperules & GTR_SPHERES) { if (pcount > 2) @@ -10313,6 +10228,10 @@ void P_RespawnSpecials(void) && !(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE)) time = (time * 3) / max(1, mapheaderinfo[gamemap-1]->numlaps); + // this should probably be a flag if multiple items end up using this + if (mthing && mthing->type == mobjinfo[MT_ITEMCAPSULE].doomednum) + time /= 2; + if (time < 10*TICRATE) { // Ensure it doesn't go into absurdly low values @@ -10321,21 +10240,10 @@ void P_RespawnSpecials(void) } } - // nothing left to respawn? - if (iquehead == iquetail) - return; - // the first item in the queue is the first to respawn if (leveltime - itemrespawntime[iquetail] < (tic_t)time) return; - mthing = itemrespawnque[iquetail]; - -#ifdef PARANOIA - if (!mthing) - I_Error("itemrespawnque[iquetail] is NULL!"); -#endif - if (mthing) P_SpawnMapThing(mthing); @@ -11801,7 +11709,9 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean // Ambush = double size (grounded) / half size (aerial) if (!(mthing->options & MTF_AMBUSH) == !P_IsObjectOnGround(mobj)) - P_SetScale(mobj, mobj->destscale = min(mobj->scale << 1, FixedDiv(64*FRACUNIT, mobj->info->radius))); // don't make them larger than the blockmap can handle + mobj->destscale = min(mobj->scale << 1, FixedDiv(64*FRACUNIT, mobj->info->radius)); // don't make them larger than the blockmap can handle + + P_RefreshItemCapsuleParts(mobj); break; } case MT_AAZTREE_HELPER: