From f031177ca40d6c5310f0e733eb9bf79beadf0c57 Mon Sep 17 00:00:00 2001 From: lachablock Date: Sat, 12 Jun 2021 14:12:45 +1000 Subject: [PATCH 01/17] Add some generalized functions for getting floating item frames --- src/k_kart.c | 10 ++++++++++ src/k_kart.h | 2 ++ src/p_mobj.c | 8 ++++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index e4f3972bc..396dc38cb 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9139,4 +9139,14 @@ void K_CheckSpectateStatus(void) } } +UINT8 K_GetInvincibilityItemFrame(void) +{ + return ((leveltime % (7*3)) / 3); +} + +UINT8 K_GetOrbinautItemFrame(UINT8 count) +{ + return min(count - 1, 3); +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index d0668450e..32a134034 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -116,6 +116,8 @@ SINT8 K_Sliptiding(player_t *player); void K_AdjustPlayerFriction(player_t *player); void K_MoveKartPlayer(player_t *player, boolean onground); void K_CheckSpectateStatus(void); +UINT8 K_GetInvincibilityItemFrame(void); +UINT8 K_GetOrbinautItemFrame(UINT8 count); // sound stuff for lua void K_PlayAttackTaunt(mobj_t *source); diff --git a/src/p_mobj.c b/src/p_mobj.c index 1bc2c7761..602566b1b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5457,11 +5457,11 @@ static void P_MobjSceneryThink(mobj_t *mobj) { case KITEM_ORBINAUT: mobj->tracer->sprite = SPR_ITMO; - mobj->tracer->frame = FF_FULLBRIGHT|(min(mobj->target->player->itemamount-1, 3)); + mobj->tracer->frame = FF_FULLBRIGHT|K_GetOrbinautItemFrame(mobj->target->player->itemamount); break; case KITEM_INVINCIBILITY: mobj->tracer->sprite = SPR_ITMI; - mobj->tracer->frame = FF_FULLBRIGHT|((leveltime % (7*3)) / 3); + mobj->tracer->frame = FF_FULLBRIGHT|K_GetInvincibilityItemFrame(); break; case KITEM_SAD: mobj->tracer->sprite = SPR_ITEM; @@ -6063,11 +6063,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { case KITEM_ORBINAUT: mobj->sprite = SPR_ITMO; - mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(min(mobj->movecount-1, 3)); + mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(mobj->movecount); break; case KITEM_INVINCIBILITY: mobj->sprite = SPR_ITMI; - mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|((leveltime % (7*3)) / 3); + mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame(); break; case KITEM_SAD: mobj->sprite = SPR_ITEM; From b78db12b0dae44c6374d7755bb7862591b56547c Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 14 Jun 2021 12:20:39 +1000 Subject: [PATCH 02/17] Add item capsules --- src/d_player.h | 2 +- src/deh_tables.c | 11 +++ src/info.c | 64 +++++++++++++ src/info.h | 12 +++ src/k_kart.c | 2 +- src/p_inter.c | 82 +++++++++++++++++ src/p_mobj.c | 233 +++++++++++++++++++++++++++++++++++++++++++++++ src/p_saveg.c | 4 +- src/sounds.c | 1 + src/sounds.h | 1 + 10 files changed, 408 insertions(+), 4 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index ba83aae60..36ceb85d4 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -421,7 +421,7 @@ typedef struct player_s UINT8 ringdelay; // (0 to 3) - 3 tic delay between every ring usage UINT16 ringboost; // Ring boost timer UINT8 sparkleanim; // (0 to 19) - Angle offset for ring sparkle animation - UINT8 superring; // Spawn rings on top of you every tic! + UINT16 superring; // Spawn rings on top of you every tic! UINT8 curshield; // see kartshields_t UINT8 bubblecool; // Bubble Shield use cooldown diff --git a/src/deh_tables.c b/src/deh_tables.c index 891f681bf..128f14726 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3484,6 +3484,15 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_ITEMICON", + // Item capsules + "S_ITEMCAPSULE", + "S_ITEMCAPSULE_TOP_SIDE", + "S_ITEMCAPSULE_BOTTOM_SIDE_AIR", + "S_ITEMCAPSULE_BOTTOM_SIDE_GROUND", + "S_ITEMCAPSULE_TOP", + "S_ITEMCAPSULE_BOTTOM", + "S_ITEMCAPSULE_INSIDE", + // Signpost sparkles "S_SIGNSPARK1", "S_SIGNSPARK2", @@ -5438,6 +5447,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_RANDOMITEM", "MT_RANDOMITEMPOP", "MT_FLOATINGITEM", + "MT_ITEMCAPSULE", + "MT_ITEMCAPSULE_PART", "MT_SIGNSPARKLE", diff --git a/src/info.c b/src/info.c index 5788d8945..0f8cd833e 100644 --- a/src/info.c +++ b/src/info.c @@ -537,6 +537,8 @@ char sprnames[NUMSPRITES + 1][5] = "KINB", // Darker invincibility sparkle trail "KINF", // Invincibility flash "INVI", // Invincibility speedlines + "ICAP", // Item capsules + "WIPD", // Wipeout dust trail "DRIF", // Drift Sparks "BDRF", // Brake drift sparks @@ -4063,6 +4065,14 @@ 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_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 + {SPR_ICAP, FF_FLOORSPRITE|3, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMCAPSULE_TOP + {SPR_ICAP, FF_FLOORSPRITE|4, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMCAPSULE_BOTTOM + {SPR_ICAP, FF_FLOORSPRITE|5, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMCAPSULE_INSIDE + {SPR_SGNS, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_SIGNSPARK2}, // S_SIGNSPARK1 {SPR_SGNS, FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_SIGNSPARK3}, // S_SIGNSPARK2 {SPR_SGNS, FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_SIGNSPARK4}, // S_SIGNSPARK3 @@ -23053,6 +23063,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_ITEMCAPSULE + 2010, // doomednum + S_ITEMCAPSULE, // 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_itcaps, // deathsound + 0, // speed + 56*FRACUNIT, // radius + 112*FRACUNIT, // height + 1, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_ITEMCAPSULE_PART + -1, // doomednum + S_INVISIBLE, // spawnstate + 1000, // 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 + 26*FRACUNIT, // radius + 14*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT, // flags + S_NULL // raisestate + }, + { // MT_SIGNSPARKLE -1, // doomednum S_SIGNSPARK1, // spawnstate diff --git a/src/info.h b/src/info.h index d15d0dde0..57f8ed5ad 100644 --- a/src/info.h +++ b/src/info.h @@ -1078,6 +1078,7 @@ typedef enum sprite SPR_KINB, // Darker invincibility sparkle trail SPR_KINF, // Invincibility flash SPR_INVI, // Invincibility speedlines + SPR_ICAP, // Item capsules SPR_WIPD, // Wipeout dust trail SPR_DRIF, // Drift Sparks @@ -4455,6 +4456,15 @@ typedef enum state S_ITEMICON, + // Item capsules + S_ITEMCAPSULE, + S_ITEMCAPSULE_TOP_SIDE, + S_ITEMCAPSULE_BOTTOM_SIDE_AIR, + S_ITEMCAPSULE_BOTTOM_SIDE_GROUND, + S_ITEMCAPSULE_TOP, + S_ITEMCAPSULE_BOTTOM, + S_ITEMCAPSULE_INSIDE, + // Signpost sparkles S_SIGNSPARK1, S_SIGNSPARK2, @@ -6446,6 +6456,8 @@ typedef enum mobj_type MT_RANDOMITEM, MT_RANDOMITEMPOP, MT_FLOATINGITEM, + MT_ITEMCAPSULE, + MT_ITEMCAPSULE_PART, MT_SIGNSPARKLE, diff --git a/src/k_kart.c b/src/k_kart.c index 396dc38cb..de3904277 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8801,7 +8801,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } break; case KITEM_SUPERRING: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO && player->superring < (UINT8_MAX - (10*3))) + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO && player->superring < (UINT16_MAX - (10*3))) { player->superring += (10*3); player->itemamount--; diff --git a/src/p_inter.c b/src/p_inter.c index a73c688f4..861cf2f84 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -277,6 +277,16 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_SetTarget(&special->target, toucher); P_KillMobj(special, toucher, toucher, DMG_NORMAL); break; + case MT_ITEMCAPSULE: + if (special->threshold != KITEM_SUPERRING && !P_CanPickupItem(player, 1)) + return; + + if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) + return; + + S_StartSound(toucher, special->info->deathsound); + P_KillMobj(special, toucher, toucher, DMG_NORMAL); + return; case MT_KARMAHITBOX: if (!special->target->player) return; @@ -1282,6 +1292,78 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget } break; + case MT_ITEMCAPSULE: + { + UINT8 i; + mobj_t *part = target->hnext; + angle_t angle = FixedAngle(360*P_RandomFixed()); + + // burst effects + for (i = 0; i < 2; i++) + { + mobj_t *blast = P_SpawnMobjFromMobj(target, 0, 0, target->info->height >> 1, MT_BATTLEBUMPER_BLAST); + blast->angle = angle + i*ANGLE_90; + P_SetScale(blast, 2*blast->scale/3); + blast->destscale = 2*blast->scale; + } + + // remove inside item + if (target->tracer && !P_MobjWasRemoved(target->tracer)) + P_RemoveMobj(target->tracer); + + // bust capsule caps + while (part && !P_MobjWasRemoved(part)) + { + mobj_t *attacker = inflictor ? inflictor : source; + P_InstaThrust(part, part->angle + ANGLE_90, 6 * part->target->scale); + P_SetObjectMomZ(part, 4 * part->scale, false); + part->fuse = TICRATE/2; + part->flags &= ~MF_NOGRAVITY; + + if (attacker) + { + part->momx += attacker->momx; + part->momy += attacker->momy; + part->momz += attacker->momz; + } + part = part->hnext; + } + + // give the player an item! + if (source && source->player) + { + player_t *player = source->player; + + // special behavior for ring capsules + if (target->threshold == KITEM_SUPERRING) + { + player->superring = min(player->superring + 5*target->movecount*3, UINT16_MAX); + break; + } + + if (target->threshold < 1 || target->threshold >= NUMKARTITEMS) // bruh moment prevention + { + player->itemtype = KITEM_SAD; + player->itemamount = 1; + } + else + { + player->itemtype = target->threshold; + if (K_GetShieldFromItem(player->itemtype) != KSHIELD_NONE) // never give more than 1 shield + player->itemamount = 1; + else + player->itemamount = max(1, target->movecount); + } + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = 0; + player->itemroulette = 0; + player->roulettetype = 0; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolf); + } + break; + } + case MT_BATTLECAPSULE: { mobj_t *cur; diff --git a/src/p_mobj.c b/src/p_mobj.c index 602566b1b..c2782d1c3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3748,6 +3748,65 @@ static void P_RingThinker(mobj_t *mobj) P_CycleMobjState(mobj); } +static void P_ItemCapsulePartThinker(mobj_t *mobj) +{ + if (mobj->fuse > 0) // dead + { + mobj->fuse--; + if (mobj->fuse == 0) + { + P_RemoveMobj(mobj); + return; + } + mobj->renderflags ^= RF_DONTDRAW; + } + else // alive + { + mobj_t *target = mobj->target; + fixed_t targetScale, z; + + if (P_MobjWasRemoved(mobj->target)) // if the capsule was removed, remove the parts too + { + 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); + else + targetScale = target->scale; + + if (mobj->scale != targetScale) + P_SetScale(mobj, mobj->destscale = targetScale); + + // find z position + K_GenericExtraFlagsNoZAdjust(mobj, target); + if (mobj->flags & MFE_VERTICALFLIP) + z = target->z + target->height - mobj->height - FixedMul(mobj->scale, mobj->movefactor); + else + z = target->z + FixedMul(mobj->scale, mobj->movefactor); + + // rotate & move to capsule + mobj->angle += mobj->movedir; + if (mobj->flags2 & MF2_CLASSICPUSH) // centered + P_TeleportMove(mobj, target->x, target->y, z); + else + P_TeleportMove(mobj, + target->x + P_ReturnThrustX(mobj, mobj->angle + ANGLE_90, mobj->radius), + target->y + P_ReturnThrustY(mobj, mobj->angle + ANGLE_90, mobj->radius), + z); + } +} + // // P_BossTargetPlayer // If closest is true, find the closest player. @@ -5589,6 +5648,9 @@ static void P_MobjSceneryThink(mobj_t *mobj) P_SetMobjStateNF(smok, smok->info->painstate); // same function, diff sprite } break; + case MT_ITEMCAPSULE_PART: + P_ItemCapsulePartThinker(mobj); + break; case MT_BATTLECAPSULE_PIECE: if (mobj->extravalue2) mobj->frame |= FF_VERTICALFLIP; @@ -6084,6 +6146,89 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } break; } + 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) + { + case KITEM_ORBINAUT: + part->sprite = SPR_ITMO; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(mobj->movecount); + break; + case KITEM_INVINCIBILITY: + part->sprite = SPR_ITMI; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame(); + break; + case KITEM_SAD: + part->sprite = SPR_ITEM; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE; + break; + default: + part->sprite = SPR_ITEM; + 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: { boolean grounded = P_IsObjectOnGround(mobj); @@ -8975,6 +9120,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) case MT_FLOATINGITEM: case MT_BLUESPHERE: case MT_EMERALD: + case MT_ITEMCAPSULE: thing->shadowscale = FRACUNIT/2; break; case MT_DRIFTCLIP: @@ -9333,6 +9479,81 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->fuse = 100; break; // 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; + +#if 0 // set to 1 to test capsules with random items, e.g. with objectplace + if (P_RandomChance(FRACUNIT/3)) + mobj->threshold = KITEM_SUPERRING; + else if (P_RandomChance(FRACUNIT/3)) + mobj->threshold = KITEM_ORBINAUT; + else + mobj->threshold = P_RandomRange(1, NUMKARTITEMS - 1); + mobj->movecount = P_RandomChance(FRACUNIT/3) ? 1 : P_RandomKey(32) + 1; +#else + mobj->threshold = KITEM_SUPERRING; // default item is super ring + mobj->movecount = 1; +#endif + + P_CheckPosition(mobj, mobj->x, mobj->y); // look for FOFs + if (P_IsObjectOnGround(mobj)) + { + mobj->flags &= ~MF_NOGRAVITY; + buttScale = 13*FRACUNIT/10; + buttState = S_ITEMCAPSULE_BOTTOM_SIDE_GROUND; + spin = 0; + } + + // 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 + P_ItemCapsulePartThinker(part); + + // 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 + P_ItemCapsulePartThinker(part); + + // 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 + P_ItemCapsulePartThinker(part); + } + break; +#undef CAPSULESIDES +#undef ANG_CAPSULE +#undef ROTATIONSPEED + } case MT_KARMAHITBOX: { const fixed_t rad = FixedMul(mobjinfo[MT_PLAYER].radius, mobj->scale); @@ -11567,6 +11788,18 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean } break; } + case MT_ITEMCAPSULE: + { + // Angle = item type + // Parameter = extra items (x5 for rings) + // Special = +16 items (+80 for rings) + if (mthing->angle > 0 && mthing->angle < NUMKARTITEMS) + mobj->threshold = mthing->angle; + if (mthing->options & MTF_OBJECTSPECIAL) + mobj->movecount += 16; + mobj->movecount += mthing->extrainfo; + break; + } case MT_AAZTREE_HELPER: { fixed_t top = mobj->z; diff --git a/src/p_saveg.c b/src/p_saveg.c index 35f68be29..39bced967 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -276,7 +276,7 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].ringdelay); WRITEUINT16(save_p, players[i].ringboost); WRITEUINT8(save_p, players[i].sparkleanim); - WRITEUINT8(save_p, players[i].superring); + WRITEUINT16(save_p, players[i].superring); WRITEUINT8(save_p, players[i].curshield); WRITEUINT8(save_p, players[i].bubblecool); @@ -528,7 +528,7 @@ static void P_NetUnArchivePlayers(void) players[i].ringdelay = READUINT8(save_p); players[i].ringboost = READUINT16(save_p); players[i].sparkleanim = READUINT8(save_p); - players[i].superring = READUINT8(save_p); + players[i].superring = READUINT16(save_p); players[i].curshield = READUINT8(save_p); players[i].bubblecool = READUINT8(save_p); diff --git a/src/sounds.c b/src/sounds.c index 636b53a8c..b46e11ba1 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -966,6 +966,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // :shitsfree: {"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Debug notification {"cock", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Hammer cocks, bang bang + {"itcaps", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, "Item capsule"}, // SRB2Kart - Engine sounds // Engine class A diff --git a/src/sounds.h b/src/sounds.h index ac7d5b9cf..8d81aae20 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1030,6 +1030,7 @@ typedef enum sfx_itfree, sfx_dbgsal, sfx_cock, + sfx_itcaps, // Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy... // Engine class A - Low Speed, Low Weight From 7e1a29310f4ee9e9ad6faf72e302780b7cd9cb5b Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 14 Jun 2021 13:47:15 +1000 Subject: [PATCH 03/17] Add item capsule Ambush flag scaling; attempted gravity fix --- src/info.c | 2 +- src/p_mobj.c | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/info.c b/src/info.c index f58759aa7..920de1d51 100644 --- a/src/info.c +++ b/src/info.c @@ -23088,7 +23088,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + MF_SLIDEME|MF_SPECIAL|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/p_mobj.c b/src/p_mobj.c index 24f1e0a03..e02376d9a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9506,11 +9506,12 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) P_CheckPosition(mobj, mobj->x, mobj->y); // look for FOFs if (P_IsObjectOnGround(mobj)) { - mobj->flags &= ~MF_NOGRAVITY; buttScale = 13*FRACUNIT/10; buttState = S_ITEMCAPSULE_BOTTOM_SIDE_GROUND; spin = 0; } + else + mobj->flags |= MF_NOGRAVITY; // inside item part = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_ITEMCAPSULE_PART); @@ -9520,7 +9521,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) 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 - P_ItemCapsulePartThinker(part); // capsule caps part = mobj; @@ -9536,7 +9536,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) part->movedir = spin; // rotation speed part->movefactor = 0; // z offset part->extravalue1 = buttScale; // relative scale - P_ItemCapsulePartThinker(part); // a top side P_SetTarget(&part->hnext, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_ITEMCAPSULE_PART)); @@ -9547,7 +9546,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) part->angle = i * ANG_CAPSULE; part->movedir = spin; // rotation speed part->movefactor = mobj->info->height - part->info->height; // z offset - P_ItemCapsulePartThinker(part); } break; #undef CAPSULESIDES @@ -11791,13 +11789,19 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean case MT_ITEMCAPSULE: { // Angle = item type - // Parameter = extra items (x5 for rings) - // Special = +16 items (+80 for rings) if (mthing->angle > 0 && mthing->angle < NUMKARTITEMS) mobj->threshold = mthing->angle; + + // Parameter = extra items (x5 for rings) + mobj->movecount += mthing->extrainfo; + + // Special = +16 items (+80 for rings) if (mthing->options & MTF_OBJECTSPECIAL) mobj->movecount += 16; - mobj->movecount += mthing->extrainfo; + + // 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 break; } case MT_AAZTREE_HELPER: From 459c5615af3c991f1d4934cc8e889f37e51e2d46 Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 14 Jun 2021 19:04:55 +1000 Subject: [PATCH 04/17] - 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: From 9151d31512bb0263e133091f418159e4e1539636 Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 14 Jun 2021 22:58:36 +1000 Subject: [PATCH 05/17] Allow only ring capsules to spawn in Record Attack, and add the ability for Extra to invert this behavior --- src/p_mobj.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index 0255f775f..da1a41f9a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10745,6 +10745,16 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) if (modifiedgame && !savemoddata) return false; // No cheating!! + break; + case MT_ITEMCAPSULE: + // in record attack, only spawn ring capsules + // (behavior can be inverted with the Extra flag, i.e. item capsule spawns and ring capsule does not) + if (modeattacking) + { + boolean isRingCapsule = (mthing->angle < 1 || mthing->angle == KITEM_SUPERRING || mthing->angle >= NUMKARTITEMS); + if (!(mthing->options & MTF_EXTRA) == !isRingCapsule) + return false; + } break; default: break; From da37ba823835e3c41dc38838ca6081c82ec03890 Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 14 Jun 2021 23:04:28 +1000 Subject: [PATCH 06/17] Allow scripts (or other outside sources) to easily change the properties of an item capsule --- src/p_enemy.c | 3 +++ src/p_mobj.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/p_enemy.c b/src/p_enemy.c index b472e8259..c7acf06dc 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -14661,6 +14661,9 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) mobj_t *part = mobj->tracer; skincolornum_t color; + part->threshold = mobj->threshold; + part->movecount = mobj->movecount; + if (itemType < 1 || itemType >= NUMKARTITEMS) itemType = KITEM_SAD; diff --git a/src/p_mobj.c b/src/p_mobj.c index da1a41f9a..cf262a78b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6144,6 +6144,10 @@ static boolean P_MobjRegularThink(mobj_t *mobj) INT32 itemType = mobj->threshold; mobj_t *part = mobj->tracer; + if (mobj->threshold != part->threshold + || mobj->movecount != part->movecount) // allow scripters to easily change the capsule properties! + P_RefreshItemCapsuleParts(mobj); + if (itemType < 1 || itemType >= NUMKARTITEMS) itemType = KITEM_SAD; From e981de3c33405fd980c86170ad6ac7e4ba7d148e Mon Sep 17 00:00:00 2001 From: lachablock Date: Tue, 15 Jun 2021 13:41:53 +1000 Subject: [PATCH 07/17] - Color Record Attack-persistent capsules sapphire - Make invincibility capsules glow rainbow - Fix aerial capsules not scaling to full size --- src/p_enemy.c | 57 ++++++++++++++++++++++++++++++++++++++++++++------- src/p_inter.c | 2 +- src/p_mobj.c | 28 +++++-------------------- 3 files changed, 56 insertions(+), 31 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index c7acf06dc..d40e449bc 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -14660,6 +14660,8 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) INT32 itemType = mobj->threshold; mobj_t *part = mobj->tracer; skincolornum_t color; + UINT32 newRenderFlags = 0; + boolean colorized; part->threshold = mobj->threshold; part->movecount = mobj->movecount; @@ -14667,6 +14669,40 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) if (itemType < 1 || itemType >= NUMKARTITEMS) itemType = KITEM_SAD; + // update invincibility properties + if (itemType == KITEM_INVINCIBILITY) + { + mobj->renderflags |= RF_FULLBRIGHT; + mobj->colorized = true; + } + else + { + mobj->renderflags &= ~RF_FULLBRIGHT; + mobj->color = SKINCOLOR_NONE; + mobj->colorized = false; + } + + // update inside item frame + switch (itemType) + { + case KITEM_ORBINAUT: + part->sprite = SPR_ITMO; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(mobj->movecount); + break; + case KITEM_INVINCIBILITY: + part->sprite = SPR_ITMI; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame(); + break; + case KITEM_SAD: + part->sprite = SPR_ITEM; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE; + break; + default: + part->sprite = SPR_ITEM; + 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 ; @@ -14714,18 +14750,25 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) P_SetTarget(&part->tracer, NULL); } - // update color - color = (itemType == KITEM_SUPERRING ? SKINCOLOR_GOLD : SKINCOLOR_NONE); + // update cap colors + if (itemType == KITEM_SUPERRING) + { + color = SKINCOLOR_GOLD; + newRenderFlags |= RF_SEMIBRIGHT; + } + else if (mobj->spawnpoint && (mobj->spawnpoint->options & MTF_EXTRA)) + color = SKINCOLOR_SAPPHIRE; + else + color = SKINCOLOR_NONE; + + colorized = (color != 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; + part->colorized = colorized; + part->renderflags = (part->renderflags & ~RF_SEMIBRIGHT) | newRenderFlags; } } diff --git a/src/p_inter.c b/src/p_inter.c index 2f98dd178..f65562882 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -284,7 +284,7 @@ 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 + if (special->scale < special->destscale) // don't break it while it's respawning return; S_StartSound(toucher, special->info->deathsound); diff --git a/src/p_mobj.c b/src/p_mobj.c index cf262a78b..a21ed0f7b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6141,35 +6141,17 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_ITEMCAPSULE: if (!P_MobjWasRemoved(mobj->tracer)) { - INT32 itemType = mobj->threshold; mobj_t *part = mobj->tracer; if (mobj->threshold != part->threshold || mobj->movecount != part->movecount) // allow scripters to easily change the capsule properties! P_RefreshItemCapsuleParts(mobj); - if (itemType < 1 || itemType >= NUMKARTITEMS) - itemType = KITEM_SAD; - - // update inside item frame - switch (itemType) + // animate invincibility capsules + if (mobj->threshold == KITEM_INVINCIBILITY) { - case KITEM_ORBINAUT: - part->sprite = SPR_ITMO; - part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(mobj->movecount); - break; - case KITEM_INVINCIBILITY: - part->sprite = SPR_ITMI; - part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame(); - break; - case KITEM_SAD: - part->sprite = SPR_ITEM; - part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE; - break; - default: - part->sprite = SPR_ITEM; - part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(itemType); - break; + mobj->color = K_RainbowColor(leveltime); + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame(); } } break; @@ -11723,7 +11705,7 @@ 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)) - 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->destscale << 1, FixedDiv(64*FRACUNIT, mobj->info->radius)); // don't make them larger than the blockmap can handle P_RefreshItemCapsuleParts(mobj); break; From 04ed130d244f37a7e59585a60c48cec0e45e28b6 Mon Sep 17 00:00:00 2001 From: lachablock Date: Tue, 15 Jun 2021 15:16:59 +1000 Subject: [PATCH 08/17] Make capsule bubbles semibright, & fix ring capsule caps not being semibright --- src/k_kart.c | 4 ++-- src/p_enemy.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index f2a2fe9d2..0a06d9542 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1916,7 +1916,7 @@ void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) K_FlipFromObject(mo, master); // visibility (usually for hyudoro) - mo->renderflags = (master->renderflags & RF_DONTDRAW); + mo->renderflags = (mo->renderflags & ~RF_DONTDRAW) | (master->renderflags & RF_DONTDRAW); } // same as above, but does not adjust Z height when flipping @@ -1927,7 +1927,7 @@ void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master) mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); // visibility (usually for hyudoro) - mo->renderflags = (master->renderflags & RF_DONTDRAW); + mo->renderflags = (mo->renderflags & ~RF_DONTDRAW) | (master->renderflags & RF_DONTDRAW); } diff --git a/src/p_enemy.c b/src/p_enemy.c index d40e449bc..61e25c153 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -14672,12 +14672,12 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) // update invincibility properties if (itemType == KITEM_INVINCIBILITY) { - mobj->renderflags |= RF_FULLBRIGHT; + mobj->renderflags = (mobj->renderflags & ~RF_BRIGHTMASK) | RF_FULLBRIGHT; mobj->colorized = true; } else { - mobj->renderflags &= ~RF_FULLBRIGHT; + mobj->renderflags = (mobj->renderflags & ~RF_BRIGHTMASK) | RF_SEMIBRIGHT; mobj->color = SKINCOLOR_NONE; mobj->colorized = false; } @@ -14768,7 +14768,7 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) part = part->hnext; part->color = color; part->colorized = colorized; - part->renderflags = (part->renderflags & ~RF_SEMIBRIGHT) | newRenderFlags; + part->renderflags = (part->renderflags & ~RF_BRIGHTMASK) | newRenderFlags; } } From 1b41db9edc08e416dfad3a21e7aa3ed98625011f Mon Sep 17 00:00:00 2001 From: lachablock Date: Wed, 16 Jun 2021 12:30:21 +1000 Subject: [PATCH 09/17] Fix mapload crash caused by capsules --- src/p_mobj.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index a21ed0f7b..0097fc01b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6144,7 +6144,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj_t *part = mobj->tracer; if (mobj->threshold != part->threshold - || mobj->movecount != part->movecount) // allow scripters to easily change the capsule properties! + || mobj->movecount != part->movecount) // change the capsule properties if the item type or amount is updated P_RefreshItemCapsuleParts(mobj); // animate invincibility capsules @@ -11706,8 +11706,6 @@ 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)) mobj->destscale = min(mobj->destscale << 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: From 65204d951cae0907deb074f068a0d7b10f11293f Mon Sep 17 00:00:00 2001 From: lachablock Date: Sun, 20 Jun 2021 15:25:48 +1000 Subject: [PATCH 10/17] Add dust to capsule burst & fix cap bust momentum --- src/p_inter.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index f65562882..e0d6d3a5c 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1298,8 +1298,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget case MT_ITEMCAPSULE: { UINT8 i; + mobj_t *attacker = inflictor ? inflictor : source; mobj_t *part = target->hnext; angle_t angle = FixedAngle(360*P_RandomFixed()); + INT16 spacing = (target->radius >> 1) / target->scale; // burst effects for (i = 0; i < 2; i++) @@ -1310,6 +1312,29 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget blast->destscale = 2*blast->scale; } + // dust effects + for (i = 0; i < 10; 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 + ); + + P_SetScale(puff, (puff->destscale *= 2)); + puff->momz = puff->scale * P_MobjFlip(puff); + + P_Thrust(puff, R_PointToAngle2(target->x, target->y, puff->x, puff->y), 3*puff->scale); + if (attacker) + { + puff->momx += attacker->momx; + puff->momy += attacker->momy; + puff->momz += attacker->momz; + } + } + // remove inside item if (target->tracer && !P_MobjWasRemoved(target->tracer)) P_RemoveMobj(target->tracer); @@ -1317,9 +1342,8 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget // bust capsule caps while (part && !P_MobjWasRemoved(part)) { - mobj_t *attacker = inflictor ? inflictor : source; P_InstaThrust(part, part->angle + ANGLE_90, 6 * part->target->scale); - P_SetObjectMomZ(part, 4 * part->scale, false); + P_SetObjectMomZ(part, 6 * FRACUNIT, false); part->fuse = TICRATE/2; part->flags &= ~MF_NOGRAVITY; From 8f93cdbb6b8722ce99066ad4653c72344f3c3723 Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 21 Jun 2021 11:42:02 +1000 Subject: [PATCH 11/17] Revert item capsule respawn queue changes --- src/p_mobj.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 0097fc01b..4bdac15d8 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10169,17 +10169,6 @@ 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++) { @@ -10214,10 +10203,6 @@ 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 @@ -10226,10 +10211,21 @@ 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); From b1e412ee33585d8e1a3e71120273947d2a7dbfc8 Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 21 Jun 2021 12:03:01 +1000 Subject: [PATCH 12/17] Address item capsule suggestoastions --- src/p_enemy.c | 58 +++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 61e25c153..dfe6f671e 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -14658,14 +14658,11 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) UINT8 numNumbers = 0; INT32 count = 0; INT32 itemType = mobj->threshold; - mobj_t *part = mobj->tracer; + mobj_t *part; skincolornum_t color; UINT32 newRenderFlags = 0; boolean colorized; - part->threshold = mobj->threshold; - part->movecount = mobj->movecount; - if (itemType < 1 || itemType >= NUMKARTITEMS) itemType = KITEM_SAD; @@ -14682,7 +14679,35 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) mobj->colorized = false; } + // update cap colors + if (itemType == KITEM_SUPERRING) + { + color = SKINCOLOR_GOLD; + newRenderFlags |= RF_SEMIBRIGHT; + } + else if (mobj->spawnpoint && (mobj->spawnpoint->options & MTF_EXTRA)) + color = SKINCOLOR_SAPPHIRE; + else + color = SKINCOLOR_NONE; + + colorized = (color != SKINCOLOR_NONE); + part = mobj; + while (!P_MobjWasRemoved(part->hnext)) + { + part = part->hnext; + part->color = color; + part->colorized = colorized; + part->renderflags = (part->renderflags & ~RF_BRIGHTMASK) | newRenderFlags; + } + // update inside item frame + part = mobj->tracer; + if (P_MobjWasRemoved(part)) + return; + + part->threshold = mobj->threshold; + part->movecount = mobj->movecount; + switch (itemType) { case KITEM_ORBINAUT: @@ -14749,27 +14774,6 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) P_RemoveMobj(part->tracer); P_SetTarget(&part->tracer, NULL); } - - // update cap colors - if (itemType == KITEM_SUPERRING) - { - color = SKINCOLOR_GOLD; - newRenderFlags |= RF_SEMIBRIGHT; - } - else if (mobj->spawnpoint && (mobj->spawnpoint->options & MTF_EXTRA)) - color = SKINCOLOR_SAPPHIRE; - else - color = SKINCOLOR_NONE; - - colorized = (color != SKINCOLOR_NONE); - part = mobj; - while (!P_MobjWasRemoved(part->hnext)) - { - part = part->hnext; - part->color = color; - part->colorized = colorized; - part->renderflags = (part->renderflags & ~RF_BRIGHTMASK) | newRenderFlags; - } } #define CAPSULESIDES 5 @@ -14781,7 +14785,7 @@ void A_SpawnItemCapsuleParts(mobj_t *actor) mobj_t *part; fixed_t buttScale = 0; statenum_t buttState = S_ITEMCAPSULE_BOTTOM_SIDE_AIR; - angle_t spin = -ROTATIONSPEED; + angle_t spin = ANGLE_MAX - ROTATIONSPEED; if (LUA_CallAction(A_SPAWNITEMCAPSULEPARTS, actor)) return; @@ -14799,7 +14803,7 @@ void A_SpawnItemCapsuleParts(mobj_t *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 + part->flags2 |= MF2_CLASSICPUSH; // classicpush = centered horizontally P_SetTarget(&actor->tracer, part); // pointer to this item, so we can modify its sprite/frame // capsule caps From 27489edd3d55e11751ea2968cb7ca45e67a9242b Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 21 Jun 2021 12:07:18 +1000 Subject: [PATCH 13/17] Don't spawn ring capsules in GTR_SPHERES gametypes --- src/p_mobj.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 4bdac15d8..5e3bac915 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10729,12 +10729,17 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) break; case MT_ITEMCAPSULE: - // in record attack, only spawn ring capsules - // (behavior can be inverted with the Extra flag, i.e. item capsule spawns and ring capsule does not) - if (modeattacking) { boolean isRingCapsule = (mthing->angle < 1 || mthing->angle == KITEM_SUPERRING || mthing->angle >= NUMKARTITEMS); - if (!(mthing->options & MTF_EXTRA) == !isRingCapsule) + + // don't spawn ring capsules in GTR_SPHERES gametypes + if (isRingCapsule && (gametyperules & GTR_SPHERES)) + return false; + + // in record attack, only spawn ring capsules + // (behavior can be inverted with the Extra flag, i.e. item capsule spawns and ring capsule does not) + if (modeattacking + && (!(mthing->options & MTF_EXTRA) == !isRingCapsule)) return false; } break; From 90269c1a91c6eee83883c4b82356c6b2be5ea146 Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 21 Jun 2021 13:11:21 +1000 Subject: [PATCH 14/17] New item capsule respawn functionality --- src/info.c | 2 +- src/p_inter.c | 8 ++++++++ src/p_mobj.c | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index ef941c34b..7f2e06f56 100644 --- a/src/info.c +++ b/src/info.c @@ -23078,7 +23078,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_NULL, // deathstate + S_INVISIBLE, // deathstate S_NULL, // xdeathstate sfx_itcaps, // deathsound 0, // speed diff --git a/src/p_inter.c b/src/p_inter.c index e0d6d3a5c..01c4b82de 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1303,6 +1303,14 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget angle_t angle = FixedAngle(360*P_RandomFixed()); INT16 spacing = (target->radius >> 1) / target->scale; + // set respawn fuse + if (modeattacking) // no respawns + ; + else if (target->threshold == KITEM_SUPERRING) + target->fuse = 20*TICRATE; + else + target->fuse = 40*TICRATE; + // burst effects for (i = 0; i < 2; i++) { diff --git a/src/p_mobj.c b/src/p_mobj.c index 5e3bac915..007d15f12 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8476,6 +8476,17 @@ static boolean P_FuseThink(mobj_t *mobj) P_RemoveMobj(mobj); // make sure they disappear return false; + case MT_ITEMCAPSULE: + if (mobj->spawnpoint) + P_SpawnMapThing(mobj->spawnpoint); + else + { + mobj_t *newMobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type); + newMobj->threshold = mobj->threshold; + newMobj->movecount = mobj->movecount; + } + P_RemoveMobj(mobj); + return false; case MT_SMK_ICEBLOCK: { mobj_t *cur = mobj->hnext, *next; @@ -11706,7 +11717,10 @@ 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)) + { mobj->destscale = min(mobj->destscale << 1, FixedDiv(64*FRACUNIT, mobj->info->radius)); // don't make them larger than the blockmap can handle + mobj->scalespeed <<= 1; + } break; } case MT_AAZTREE_HELPER: From baae615d0840be54689a196fd7b898722ed026dd Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 21 Jun 2021 15:10:57 +1000 Subject: [PATCH 15/17] Capsules FINALLY spawn properly on FOFs, and aerial capsules scale from the bottoms instead of their centers --- src/p_inter.c | 2 +- src/p_mobj.c | 32 ++++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 01c4b82de..ea2e3c348 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -284,7 +284,7 @@ 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 + if (special->scale < special->extravalue1) // don't break it while it's respawning return; S_StartSound(toucher, special->info->deathsound); diff --git a/src/p_mobj.c b/src/p_mobj.c index 007d15f12..8d9429b8f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6139,6 +6139,21 @@ static boolean P_MobjRegularThink(mobj_t *mobj) break; } case MT_ITEMCAPSULE: + // scale the capsule + if (mobj->scale < mobj->extravalue1) + { + fixed_t oldHeight = mobj->height; + + if ((mobj->extravalue1 - mobj->scale) < mobj->scalespeed) + P_SetScale(mobj, mobj->destscale = mobj->extravalue1); + else + P_SetScale(mobj, mobj->destscale = mobj->scale + mobj->scalespeed); + + if (mobj->eflags & MFE_VERTICALFLIP) + mobj->z -= (mobj->height - oldHeight); + } + + // update & animate capsule if (!P_MobjWasRemoved(mobj->tracer)) { mobj_t *part = mobj->tracer; @@ -9434,17 +9449,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 + P_AdjustMobjFloorZ_FFloors(mobj, mobj->subsector->sector, 0); if (!P_IsObjectOnGround(mobj)) mobj->flags |= MF_NOGRAVITY; + // set starting scale + mobj->extravalue1 = mobj->scale; // this acts as the capsule's destscale; we're avoiding P_MobjScaleThink because we want aerial capsules not to scale from their center + mobj->scalespeed >>= 1; + P_SetScale(mobj, mobj->destscale = mapobjectscale >> 4); + if (mobj->eflags & MFE_VERTICALFLIP) + mobj->z += (oldHeight - mobj->height); + break; } case MT_KARMAHITBOX: @@ -11718,7 +11734,7 @@ 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)) { - mobj->destscale = min(mobj->destscale << 1, FixedDiv(64*FRACUNIT, mobj->info->radius)); // don't make them larger than the blockmap can handle + mobj->extravalue1 = min(mobj->extravalue1 << 1, FixedDiv(64*FRACUNIT, mobj->info->radius)); // don't make them larger than the blockmap can handle mobj->scalespeed <<= 1; } break; From 6477176c310d9a37d3f4bbb820bb21ad6952645d Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 21 Jun 2021 15:33:38 +1000 Subject: [PATCH 16/17] Don't add item capsules to the respawn queue --- src/p_mobj.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 8d9429b8f..2621d8530 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9787,8 +9787,7 @@ void P_RemoveMobj(mobj_t *mobj) // Rings only, please! if (mobj->spawnpoint && (mobj->type == MT_RING - || mobj->type == MT_BLUESPHERE - || mobj->type == MT_ITEMCAPSULE) + || mobj->type == MT_BLUESPHERE) && !(mobj->flags2 & MF2_DONTRESPAWN)) { itemrespawnque[iquehead] = mobj->spawnpoint; From 05749b977339f2da113f3f3b6ef86a3f43c46d2f Mon Sep 17 00:00:00 2001 From: lachablock Date: Mon, 21 Jun 2021 22:39:21 +1000 Subject: [PATCH 17/17] Add special behavior for SPB item capsules --- src/k_kart.c | 34 +++++++++++++++++++++++++++++++++- src/k_kart.h | 2 ++ src/p_enemy.c | 3 +++ src/p_inter.c | 16 ++++++++++++++-- src/p_mobj.c | 2 ++ 5 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 0a06d9542..7074fe42a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4470,7 +4470,7 @@ static mobj_t *K_FindLastTrailMobj(player_t *player) return trail; } -static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow) +mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow) { mobj_t *mo; INT32 dir; @@ -9419,4 +9419,36 @@ UINT8 K_GetOrbinautItemFrame(UINT8 count) return min(count - 1, 3); } +boolean K_IsSPBInGame(void) +{ + UINT8 i; + thinker_t *think; + + // is there an SPB chasing anyone? + if (spbplace != -1) + return true; + + // do any players have an SPB in their item slot? + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (players[i].itemtype == KITEM_SPB) + return true; + } + + // spbplace is still -1 until a fired SPB finds a target, so look for an in-map SPB just in case + for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next) + { + if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + if (((mobj_t *)think)->type == MT_SPB) + return true; + } + + return false; +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index 67bff7241..0694488d3 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -74,6 +74,7 @@ void K_SpawnSparkleTrail(mobj_t *mo); void K_SpawnWipeoutTrail(mobj_t *mo, boolean offroad); void K_SpawnDraftDust(mobj_t *mo); void K_DriftDustHandling(mobj_t *spawner); +mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow); void K_PuntMine(mobj_t *mine, mobj_t *punter); void K_DoSneaker(player_t *player, INT32 type); void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound); @@ -120,6 +121,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground); void K_CheckSpectateStatus(void); UINT8 K_GetInvincibilityItemFrame(void); UINT8 K_GetOrbinautItemFrame(UINT8 count); +boolean K_IsSPBInGame(void); // sound stuff for lua void K_PlayAttackTaunt(mobj_t *source); diff --git a/src/p_enemy.c b/src/p_enemy.c index dfe6f671e..9f5fc86d8 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -14687,6 +14687,8 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) } else if (mobj->spawnpoint && (mobj->spawnpoint->options & MTF_EXTRA)) color = SKINCOLOR_SAPPHIRE; + else if (itemType == KITEM_SPB) + color = SKINCOLOR_JET; else color = SKINCOLOR_NONE; @@ -14743,6 +14745,7 @@ void P_RefreshItemCapsuleParts(mobj_t *mobj) count = mobj->movecount * 5; break; case KITEM_SAD: // never display the number + case KITEM_SPB: break; default: if (mobj->movecount > 1) diff --git a/src/p_inter.c b/src/p_inter.c index ea2e3c348..968357096 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -278,8 +278,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_KillMobj(special, toucher, toucher, DMG_NORMAL); break; case MT_ITEMCAPSULE: - if (special->threshold != KITEM_SUPERRING && !P_CanPickupItem(player, 1)) - return; + if (special->threshold != KITEM_SUPERRING + && special->threshold != KITEM_SPB + && !P_CanPickupItem(player, 1)) + return; if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) return; @@ -287,6 +289,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->scale < special->extravalue1) // don't break it while it's respawning return; + if (special->threshold == KITEM_SPB && K_IsSPBInGame()) // don't spawn a second SPB + return; + S_StartSound(toucher, special->info->deathsound); P_KillMobj(special, toucher, toucher, DMG_NORMAL); return; @@ -1376,6 +1381,13 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget break; } + // special behavior for SPB capsules + if (target->threshold == KITEM_SPB) + { + K_ThrowKartItem(player, true, MT_SPB, 1, 0); + break; + } + if (target->threshold < 1 || target->threshold >= NUMKARTITEMS) // bruh moment prevention { player->itemtype = KITEM_SAD; diff --git a/src/p_mobj.c b/src/p_mobj.c index 2621d8530..91f8c9403 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9439,6 +9439,8 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) #if 0 // set to 1 to test capsules with random items, e.g. with objectplace if (P_RandomChance(FRACUNIT/3)) mobj->threshold = KITEM_SUPERRING; + else if (P_RandomChance(FRACUNIT/3)) + mobj->threshold = KITEM_SPB; else if (P_RandomChance(FRACUNIT/3)) mobj->threshold = KITEM_ORBINAUT; else