From 5e03d40b3320bcf66d3333ec52cd24f232b33d12 Mon Sep 17 00:00:00 2001 From: lachwright Date: Mon, 4 Nov 2019 16:28:44 +0800 Subject: [PATCH] New end-of-level signpost logic --- src/dehacked.c | 61 +++------------- src/info.c | 74 ++++--------------- src/info.h | 61 +++------------- src/p_enemy.c | 188 ++++++++++++++++++++++++++++++++++++++++--------- src/p_mobj.c | 7 +- src/p_spec.c | 6 +- 6 files changed, 193 insertions(+), 204 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 34ee1f170..2b3194f1c 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2308,6 +2308,7 @@ static actionpointer_t actionpointers[] = {{A_ThrownRing}, "A_THROWNRING"}, {{A_SetSolidSteam}, "A_SETSOLIDSTEAM"}, {{A_UnsetSolidSteam}, "A_UNSETSOLIDSTEAM"}, + {{A_SignSpin}, "S_SIGNSPIN"}, {{A_SignPlayer}, "A_SIGNPLAYER"}, {{A_OverlayThink}, "A_OVERLAYTHINK"}, {{A_JetChase}, "A_JETCHASE"}, @@ -5385,59 +5386,13 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_BUBBLES4", // Level End Sign - "S_SIGN1", - "S_SIGN2", - "S_SIGN3", - "S_SIGN4", - "S_SIGN5", - "S_SIGN6", - "S_SIGN7", - "S_SIGN8", - "S_SIGN9", - "S_SIGN10", - "S_SIGN11", - "S_SIGN12", - "S_SIGN13", - "S_SIGN14", - "S_SIGN15", - "S_SIGN16", - "S_SIGN17", - "S_SIGN18", - "S_SIGN19", - "S_SIGN20", - "S_SIGN21", - "S_SIGN22", - "S_SIGN23", - "S_SIGN24", - "S_SIGN25", - "S_SIGN26", - "S_SIGN27", - "S_SIGN28", - "S_SIGN29", - "S_SIGN30", - "S_SIGN31", - "S_SIGN32", - "S_SIGN33", - "S_SIGN34", - "S_SIGN35", - "S_SIGN36", - "S_SIGN37", - "S_SIGN38", - "S_SIGN39", - "S_SIGN40", - "S_SIGN41", - "S_SIGN42", - "S_SIGN43", - "S_SIGN44", - "S_SIGN45", - "S_SIGN46", - "S_SIGN47", - "S_SIGN48", - "S_SIGN49", - "S_SIGN50", - "S_SIGN51", - "S_SIGN52", // Eggman - "S_SIGN53", + "S_SIGN", + "S_SIGNSPIN", + "S_SIGNSPINLOOP", + "S_SIGNROULETTE", + "S_SIGNPLAYER", + "S_SIGNBOARD", + "S_EGGMANSIGN", // Spike Ball "S_SPIKEBALL1", diff --git a/src/info.c b/src/info.c index cfaad552d..23048e49c 100644 --- a/src/info.c +++ b/src/info.c @@ -808,7 +808,7 @@ state_t states[NUMSTATES] = {SPR_PLAY, SPR2_LIFE, 20, {NULL}, 0, 4, S_NULL}, // S_PLAY_ICON3 // Level end sign (uses player sprite) - {SPR_PLAY, SPR2_SIGN, 1, {NULL}, 0, 24, S_PLAY_SIGN}, // S_PLAY_SIGN + {SPR_PLAY, SPR2_SIGN|FF_PAPERSPRITE, -1, {NULL}, 0, 29, S_PLAY_SIGN}, // S_PLAY_SIGN // NiGHTS Player, transforming {SPR_PLAY, SPR2_TRNS|FF_ANIMATE, 7, {NULL}, 0, 4, S_PLAY_NIGHTS_TRANS2}, // S_PLAY_NIGHTS_TRANS1 @@ -1925,59 +1925,13 @@ state_t states[NUMSTATES] = {SPR_BBLS, 3, 8, {A_BubbleCheck}, 0, 0, S_BUBBLES1}, // S_BUBBLES4 // Level End Sign - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN2}, // S_SIGN1 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN3}, // S_SIGN2 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN4}, // S_SIGN3 - {SPR_SIGN, 5, 1, {NULL}, 0, 0, S_SIGN5}, // S_SIGN4 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN6}, // S_SIGN5 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN7}, // S_SIGN6 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN8}, // S_SIGN7 - {SPR_SIGN, 3, 1, {NULL}, 0, 0, S_SIGN9}, // S_SIGN8 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN10}, // S_SIGN9 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN11}, // S_SIGN10 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN12}, // S_SIGN11 - {SPR_SIGN, 4, 1, {NULL}, 0, 0, S_SIGN13}, // S_SIGN12 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN14}, // S_SIGN13 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN15}, // S_SIGN14 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN16}, // S_SIGN15 - {SPR_SIGN, 3, 1, {NULL}, 0, 0, S_SIGN17}, // S_SIGN16 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN18}, // S_SIGN17 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN19}, // S_SIGN18 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN20}, // S_SIGN19 - {SPR_SIGN, 6, 1, {NULL}, 0, 0, S_SIGN21}, // S_SIGN20 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN22}, // S_SIGN21 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN23}, // S_SIGN22 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN24}, // S_SIGN23 - {SPR_SIGN, 3, 1, {NULL}, 0, 0, S_SIGN25}, // S_SIGN24 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN26}, // S_SIGN25 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN27}, // S_SIGN26 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN28}, // S_SIGN27 - {SPR_SIGN, 5, 1, {NULL}, 0, 0, S_SIGN29}, // S_SIGN28 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN30}, // S_SIGN29 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN31}, // S_SIGN30 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN32}, // S_SIGN31 - {SPR_SIGN, 3, 1, {NULL}, 0, 0, S_SIGN33}, // S_SIGN32 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN34}, // S_SIGN33 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN35}, // S_SIGN34 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN36}, // S_SIGN35 - {SPR_SIGN, 4, 1, {NULL}, 0, 0, S_SIGN37}, // S_SIGN36 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN38}, // S_SIGN37 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN39}, // S_SIGN38 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN40}, // S_SIGN39 - {SPR_SIGN, 3, 1, {NULL}, 0, 0, S_SIGN41}, // S_SIGN40 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN42}, // S_SIGN41 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN43}, // S_SIGN42 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN44}, // S_SIGN43 - {SPR_SIGN, 6, 1, {NULL}, 0, 0, S_SIGN45}, // S_SIGN44 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN46}, // S_SIGN45 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN47}, // S_SIGN46 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN48}, // S_SIGN47 - {SPR_SIGN, 3, 1, {NULL}, 0, 0, S_SIGN49}, // S_SIGN48 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN50}, // S_SIGN49 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN51}, // S_SIGN50 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN53}, // S_SIGN51 - {SPR_SIGN, 3, -1, {NULL}, 0, 0, S_NULL}, // S_SIGN52 Eggman - {SPR_SIGN, 7, -1, {A_SignPlayer}, 0, 0, S_NULL}, // S_SIGN53 Blank + {SPR_SIGN, 0, -1, {A_SignPlayer}, -3, 0, S_NULL}, // S_SIGN + {SPR_SIGN, 0, 1, {A_SignSpin}, 30, S_SIGNPLAYER, S_SIGNSPINLOOP}, // S_SIGNSPIN + {SPR_SIGN, 0, 0, {A_Repeat}, 4, S_SIGNSPIN, S_SIGNROULETTE}, // S_SIGNSPINLOOP + {SPR_SIGN, 0, 0, {A_SignPlayer}, -2, 0, S_SIGNSPIN}, // S_SIGNROULETTE + {SPR_SIGN, 0, -1, {A_SignPlayer}, -1, 0, S_NULL}, // S_SIGNPLAYER + {SPR_SIGN, FF_PAPERSPRITE|2, -1, {NULL}, 0, 0, S_NULL}, // S_SIGNBOARD + {SPR_SIGN, FF_PAPERSPRITE|1, -1, {NULL}, 0, 29, S_NULL}, // S_EGGMANSIGN // Spike Ball {SPR_SPIK, 0, 1, {NULL}, 0, 0, S_SPIKEBALL2}, // S_SPIKEBALL1 @@ -7832,29 +7786,29 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_SIGN 501, // doomednum - S_SIGN52, // spawnstate + S_SIGN, // spawnstate 1000, // spawnhealth S_PLAY_SIGN, // seestate sfx_lvpass, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance + MT_SPARK, // painchance sfx_None, // painsound - S_NULL, // meleestate + S_EGGMANSIGN, // meleestate S_NULL, // missilestate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 8, // speed - 8*FRACUNIT, // radius + 32*FRACUNIT, // radius 32*FRACUNIT, // height 0, // display offset 16, // mass 0, // damage sfx_None, // activesound - MF_NOCLIP|MF_SCENERY, // flags - S_NULL // raisestate + MF_NOCLIP|MF_SCENERY|MF_BOUNCE|MF_RUNSPAWNFUNC, // flags + S_SIGNBOARD // raisestate }, { // MT_SPIKEBALL diff --git a/src/info.h b/src/info.h index e7f41f585..c23d05ebc 100644 --- a/src/info.h +++ b/src/info.h @@ -63,6 +63,7 @@ void A_FishJump(); // Fish Jump void A_ThrownRing(); // Sparkle trail for red ring void A_SetSolidSteam(); void A_UnsetSolidSteam(); +void A_SignSpin(); void A_SignPlayer(); void A_OverlayThink(); void A_JetChase(); @@ -2058,59 +2059,13 @@ typedef enum state S_BUBBLES4, // Level End Sign - S_SIGN1, - S_SIGN2, - S_SIGN3, - S_SIGN4, - S_SIGN5, - S_SIGN6, - S_SIGN7, - S_SIGN8, - S_SIGN9, - S_SIGN10, - S_SIGN11, - S_SIGN12, - S_SIGN13, - S_SIGN14, - S_SIGN15, - S_SIGN16, - S_SIGN17, - S_SIGN18, - S_SIGN19, - S_SIGN20, - S_SIGN21, - S_SIGN22, - S_SIGN23, - S_SIGN24, - S_SIGN25, - S_SIGN26, - S_SIGN27, - S_SIGN28, - S_SIGN29, - S_SIGN30, - S_SIGN31, - S_SIGN32, - S_SIGN33, - S_SIGN34, - S_SIGN35, - S_SIGN36, - S_SIGN37, - S_SIGN38, - S_SIGN39, - S_SIGN40, - S_SIGN41, - S_SIGN42, - S_SIGN43, - S_SIGN44, - S_SIGN45, - S_SIGN46, - S_SIGN47, - S_SIGN48, - S_SIGN49, - S_SIGN50, - S_SIGN51, - S_SIGN52, // Eggman - S_SIGN53, + S_SIGN, + S_SIGNSPIN, + S_SIGNSPINLOOP, + S_SIGNROULETTE, + S_SIGNPLAYER, + S_SIGNBOARD, + S_EGGMANSIGN, // Spike Ball S_SPIKEBALL1, diff --git a/src/p_enemy.c b/src/p_enemy.c index cc2d64e8b..a6860e823 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -129,6 +129,7 @@ void A_FishJump(mobj_t *actor); void A_ThrownRing(mobj_t *actor); void A_SetSolidSteam(mobj_t *actor); void A_UnsetSolidSteam(mobj_t *actor); +void A_SignSpin(mobj_t *actor); void A_SignPlayer(mobj_t *actor); void A_OverlayThink(mobj_t *actor); void A_JetChase(mobj_t *actor); @@ -5006,59 +5007,176 @@ void A_UnsetSolidSteam(mobj_t *actor) actor->flags |= MF_NOCLIP; } +// Function: A_SignSpin +// +// Description: Spins a signpost until it hits the ground and reaches its mapthing's angle. +// +// var1 = degrees to rotate object +// var2 = state to set object to once spinning stops +// +void A_SignSpin(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SignSpin", actor)) + return; +#endif + INT32 locvar1 = var1; + INT32 locvar2 = var2; + INT16 i; + angle_t rotateangle = FixedAngle(locvar1 << FRACBITS); + + if (P_IsObjectOnGround(actor) && P_MobjFlip(actor) * actor->momz <= 0) + { + if (actor->spawnpoint) + { + angle_t mapangle = FixedAngle(actor->spawnpoint->angle << FRACBITS); + angle_t diff = mapangle - actor->angle; + if (diff < rotateangle) + { + actor->angle = mapangle; + P_SetMobjState(actor, locvar2); + return; + } + } + else // no mapthing? just finish in your current angle + { + P_SetMobjState(actor, locvar2); + return; + } + } + actor->angle += rotateangle; + if (leveltime & 1 || actor->tracer == NULL) return; + for (i = -1; i < 2; i += 2) + { + P_SpawnMobjFromMobj(actor, + P_ReturnThrustX(actor, actor->tracer->angle, i * actor->radius), + P_ReturnThrustY(actor, actor->tracer->angle, i * actor->radius), + (actor->eflags & MFE_VERTICALFLIP) ? 0 : actor->height, + actor->info->painchance); + } +} + // Function: A_SignPlayer // // Description: Changes the state of a level end sign to reflect the player that hit it. +// Also used to display Eggman or the skin roulette whilst spinning. // -// var1 = unused +// var1 = number of skin to display (e.g. 2 = Knuckles; special cases: -1 = target's skin, -2 = skin roulette, -3 = Eggman) // var2 = unused // void A_SignPlayer(mobj_t *actor) { + INT32 locvar1 = var1; + INT32 locvar2 = var2; + skin_t *skin = NULL; mobj_t *ov; - skin_t *skin; + UINT8 facecolor, signcolor; + UINT32 signframe = states[actor->info->raisestate].frame; + #ifdef HAVE_BLUA if (LUA_CallAction("A_SignPlayer", actor)) return; #endif - if (!actor->target) + + if (actor->tracer == NULL || locvar1 < -3 || locvar1 >= numskins) return; - if (!actor->target->player) - return; - - skin = &skins[actor->target->player->skin]; - - if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor? + // if no face overlay, spawn one + if (actor->tracer->tracer == NULL || P_MobjWasRemoved(actor->tracer->tracer)) { - actor->color = skin->prefoppositecolor; - /* - If you're here from the comment above Color_Opposite, - the following line is the one which is dependent on the - array being symmetrical. It gets the opposite of the - opposite of your desired colour just so it can get the - brightness frame for the End Sign. It's not a great - design choice, but it's constant time array access and - the idea that the colours should be OPPOSITES is kind - of in the name. If you have a better idea, feel free - to let me know. ~toast 2016/07/20 - */ - actor->frame += (15 - Color_Opposite[Color_Opposite[skin->prefoppositecolor - 1][0] - 1][1]); - } - else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor. - { - actor->color = Color_Opposite[actor->target->player->skincolor - 1][0]; - actor->frame += (15 - Color_Opposite[actor->target->player->skincolor - 1][1]); - } - - if (skin->sprites[SPR2_SIGN].numframes) - { - // spawn an overlay of the player's face. ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY); - P_SetTarget(&ov->target, actor); - ov->color = actor->target->player->skincolor; + P_SetTarget(&ov->target, actor->tracer); + P_SetTarget(&actor->tracer->tracer, ov); + } + else + ov = actor->tracer->tracer; + + if (locvar1 == -1) // set to target's skin + { + if (!actor->target) + return; + + if (!actor->target->player) + return; + + skin = &skins[actor->target->player->skin]; + facecolor = actor->target->player->skincolor; + + if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor? + { + signcolor = skin->prefoppositecolor; + /* + If you're here from the comment above Color_Opposite, + the following line is the one which is dependent on the + array being symmetrical. It gets the opposite of the + opposite of your desired colour just so it can get the + brightness frame for the End Sign. It's not a great + design choice, but it's constant time array access and + the idea that the colours should be OPPOSITES is kind + of in the name. If you have a better idea, feel free + to let me know. ~toast 2016/07/20 + */ + signframe += (15 - Color_Opposite[Color_Opposite[skin->prefoppositecolor - 1][0] - 1][1]); + } + else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor. + { + signcolor = Color_Opposite[actor->target->player->skincolor - 1][0]; + signframe += (15 - Color_Opposite[actor->target->player->skincolor - 1][1]); + } + } + else if (locvar1 != -3) // set to a defined skin + { + // I turned this function into a fucking mess. I'm so sorry. -Lach + if (locvar1 == -2) // next skin + { + if (ov->skin == NULL) // start at Sonic + skin = &skins[0]; + else + { + UINT8 skinnum = (skin_t*)ov->skin-skins + 1; + player_t *player = actor->target ? actor->target->player : NULL; + while (skinnum < numskins && (player ? !R_SkinUsable(player-players, skinnum) : skins[skinnum].availability > 0)) + skinnum++; + if (skinnum < numskins) + skin = &skins[skinnum]; + // leave skin NULL if we go past the skin count, this will display Eggman + } + } + else // specific skin + { + skin = &skins[locvar1]; + } + + if (skin != NULL) // if not Eggman, get the appropriate colors + { + facecolor = skin->prefcolor; + if (skin->prefoppositecolor) + { + signcolor = skin->prefoppositecolor; + } + else + { + signcolor = Color_Opposite[facecolor - 1][0]; + } + signframe += (15 - Color_Opposite[Color_Opposite[signcolor - 1][0] - 1][1]); + } + } + + if (skin != NULL && skin->sprites[SPR2_SIGN].numframes) // player face + { + ov->color = facecolor; ov->skin = skin; P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN + actor->tracer->color = signcolor; + actor->tracer->frame = signframe; + } + else // Eggman face + { + ov->color = SKINCOLOR_NONE; + ov->skin = NULL; + P_SetMobjState(ov, actor->info->meleestate); // S_EGGMANSIGN + actor->tracer->color = signcolor = SKINCOLOR_CARBON; + actor->tracer->frame = signframe += (15 - Color_Opposite[Color_Opposite[signcolor - 1][0] - 1][1]); } } @@ -5106,7 +5224,7 @@ void A_OverlayThink(mobj_t *actor) actor->z = actor->target->z + actor->target->height - mobjinfo[actor->type].height - ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT; else actor->z = actor->target->z + ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT; - actor->angle = actor->target->angle; + actor->angle = actor->target->angle + actor->movedir; actor->eflags = actor->target->eflags; actor->momx = actor->target->momx; diff --git a/src/p_mobj.c b/src/p_mobj.c index 5735dc27b..171ce2e36 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6914,7 +6914,7 @@ void P_RunOverlays(void) mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP) | (mo->target->eflags & MFE_VERTICALFLIP); mo->scale = mo->destscale = mo->target->scale; - mo->angle = mo->target->angle; + mo->angle = mo->target->angle + mo->movedir; if (!(mo->state->frame & FF_ANIMATE)) zoffs = FixedMul(((signed)mo->state->var2)*FRACUNIT, mo->scale); @@ -10441,6 +10441,11 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->extravalue2 = 0; mobj->fuse = 100; break; + case MT_SIGN: + P_SetTarget(&mobj->tracer, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY)); + P_SetTarget(&mobj->tracer->target, mobj); + P_SetMobjState(mobj->tracer, S_SIGNBOARD); + mobj->tracer->movedir = ANGLE_90; default: break; } diff --git a/src/p_spec.c b/src/p_spec.c index 50939ae5b..db8ab930c 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4058,7 +4058,8 @@ void P_SetupSignExit(player_t *player) continue; P_SetTarget(&thing->target, player->mo); - P_SetMobjState(thing, S_SIGN1); + P_SetObjectMomZ(thing, 12*FRACUNIT, false); + P_SetMobjState(thing, S_SIGNSPIN); if (thing->info->seesound) S_StartSound(thing, thing->info->seesound); @@ -4083,7 +4084,8 @@ void P_SetupSignExit(player_t *player) continue; P_SetTarget(&thing->target, player->mo); - P_SetMobjState(thing, S_SIGN1); + P_SetObjectMomZ(thing, 12*FRACUNIT, false); + P_SetMobjState(thing, S_SIGNSPIN); if (thing->info->seesound) S_StartSound(thing, thing->info->seesound);