- 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
This commit is contained in:
lachablock 2021-06-14 19:04:55 +10:00
parent 7e1a29310f
commit 459c5615af
7 changed files with 182 additions and 128 deletions

View file

@ -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"},

View file

@ -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
},

View file

@ -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];

View file

@ -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

View file

@ -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;

View file

@ -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
//

View file

@ -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: