diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 5b3a9124f..4a83f9b27 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3366,7 +3366,10 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum) if (!P_IsObjectOnGround(players[respawnplayer].mo)) return; - P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 1, DMG_DEATHPIT); + if (K_PlayerEBrake(&players[respawnplayer])) + K_SpawnRingShooter(&players[respawnplayer]); + else + P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 1, DMG_DEATHPIT); demo_extradata[playernum] |= DXD_RESPAWN; } } diff --git a/src/deh_tables.c b/src/deh_tables.c index bdb74b55b..b5746272d 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -326,6 +326,7 @@ actionpointer_t actionpointers[] = {{A_FlameShieldPaper}, "A_FLAMESHIELDPAPER"}, {{A_InvincSparkleRotate}, "A_INVINCSPARKLEROTATE"}, {{A_SpawnItemDebrisCloud}, "A_SPAWNITEMDEBRISCLOUD"}, + {{A_RingShooterFace}, "A_RINGSHOOTERFACE"}, {{NULL}, "NONE"}, @@ -3867,6 +3868,15 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_SMOOTHLANDING", + // DEZ Ring Shooter + "S_TIREGRABBER", + "S_RINGSHOOTER_SIDE", + "S_RINGSHOOTER_NIPPLES", + "S_RINGSHOOTER_SCREEN", + "S_RINGSHOOTER_NUMBERBACK", + "S_RINGSHOOTER_NUMBERFRONT", + "S_RINGSHOOTER_FACE", + // DEZ respawn laser "S_DEZLASER", "S_DEZLASER_TRAIL1", @@ -5433,6 +5443,11 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_SMOOTHLANDING", + "MT_TIREGRABBER", + "MT_RINGSHOOTER", + "MT_RINGSHOOTER_PART", + "MT_RINGSHOOTER_SCREEN", + "MT_DEZLASER", "MT_WAYPOINT", diff --git a/src/info.c b/src/info.c index 83cf21e49..7d35232fd 100644 --- a/src/info.c +++ b/src/info.c @@ -606,6 +606,10 @@ char sprnames[NUMSPRITES + 1][5] = "TWBS", // Tripwire Boost "TWBT", // Tripwire BLASTER "SMLD", // Smooth landing + + "TIRG", // Tire grabbers + "RSHT", // DEZ Ring Shooter + "DEZL", // DEZ Laser respawn // Additional Kart Objects @@ -4464,6 +4468,14 @@ state_t states[NUMSTATES] = {SPR_SMLD, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE, -1, {NULL}, 7, 2, S_NULL}, // S_SMOOTHLANDING + {SPR_TIRG, FF_ANIMATE, -1, {NULL}, 1, 1, S_NULL}, // S_TIREGRABBER + {SPR_RSHT, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_SIDE + {SPR_RSHT, FF_SEMIBRIGHT|FF_PAPERSPRITE|2, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_NIPPLES + {SPR_RSHT, FF_PAPERSPRITE|4, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_SCREEN + {SPR_RSHT, FF_FULLBRIGHT|FF_PAPERSPRITE|8, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_NUMBERBACK + {SPR_RSHT, FF_FULLBRIGHT|FF_PAPERSPRITE|12, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_NUMBERFRONT + {SPR_PLAY, FF_FULLBRIGHT|FF_PAPERSPRITE|SPR2_XTRA, -1, {A_RingShooterFace}, 0, 0, S_NULL}, // S_RINGSHOOTER_FACE + {SPR_DEZL, FF_FULLBRIGHT|FF_PAPERSPRITE, 8, {NULL}, 0, 0, S_NULL}, // S_DEZLASER {SPR_DEZL, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL2}, // S_DEZLASER_TRAIL1 {SPR_DEZL, FF_FULLBRIGHT|2, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL3}, // S_DEZLASER_TRAIL2 @@ -24623,6 +24635,114 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_TIREGRABBER + -1, // doomednum + S_TIREGRABBER, // 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 + 20*FRACUNIT, // radius + 36*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_RINGSHOOTER + -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 + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_RINGSHOOTER_PART + -1, // doomednum + S_RINGSHOOTER_SIDE, // 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 + 6*FRACUNIT, // radius + 70*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_RINGSHOOTER_SCREEN + -1, // doomednum + S_RINGSHOOTER_SCREEN, // 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 + 23*FRACUNIT, // radius + 39*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_DEZLASER -1, // doomednum S_DEZLASER, // spawnstate diff --git a/src/info.h b/src/info.h index 6c9d7c773..d80c9798a 100644 --- a/src/info.h +++ b/src/info.h @@ -294,6 +294,7 @@ enum actionnum A_FLAMESHIELDPAPER, A_INVINCSPARKLEROTATE, A_SPAWNITEMDEBRISCLOUD, + A_RINGSHOOTERFACE, NUMACTIONS }; @@ -568,6 +569,7 @@ void A_MementosTPParticles(); void A_FlameShieldPaper(); void A_InvincSparkleRotate(); void A_SpawnItemDebrisCloud(); +void A_RingShooterFace(); extern boolean actionsoverridden[NUMACTIONS]; @@ -1157,6 +1159,10 @@ typedef enum sprite SPR_TWBS, // Tripwire Boost SPR_TWBT, // Tripwire BLASTER SPR_SMLD, // Smooth landing + + SPR_TIRG, // Tire grabbers + SPR_RSHT, // DEZ Ring Shooter + SPR_DEZL, // DEZ Laser respawn // Additional Kart Objects @@ -4906,6 +4912,15 @@ typedef enum state S_SMOOTHLANDING, + // DEZ Ring Shooter + S_TIREGRABBER, + S_RINGSHOOTER_SIDE, + S_RINGSHOOTER_NIPPLES, + S_RINGSHOOTER_SCREEN, + S_RINGSHOOTER_NUMBERBACK, + S_RINGSHOOTER_NUMBERFRONT, + S_RINGSHOOTER_FACE, + // DEZ Laser respawn S_DEZLASER, S_DEZLASER_TRAIL1, @@ -6508,6 +6523,11 @@ typedef enum mobj_type MT_SMOOTHLANDING, + MT_TIREGRABBER, + MT_RINGSHOOTER, + MT_RINGSHOOTER_PART, + MT_RINGSHOOTER_SCREEN, + MT_DEZLASER, MT_WAYPOINT, diff --git a/src/k_kart.c b/src/k_kart.c index 9af81605f..4f8658ecf 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11774,3 +11774,88 @@ boolean K_Cooperative(void) } //} + +void K_SpawnRingShooter(player_t *player) +{ + const fixed_t scale = 2*FRACUNIT; + mobjinfo_t *info = &mobjinfo[MT_RINGSHOOTER_PART]; + mobj_t *mo = player->mo; + mobj_t *base = P_SpawnMobj(mo->x, mo->y, mo->z, MT_RINGSHOOTER); + mobj_t *part; + UINT32 frameNum; + angle_t angle; + vector2_t offset; + SINT8 i; + + K_FlipFromObject(base, mo); + P_SetTarget(&base->target, mo); + P_SetScale(base, base->destscale = FixedMul(base->destscale, scale)); + P_InitAngle(base, mo->angle); + + // spawn the RING NIPPLES + part = base; + frameNum = 0; + FV2_Load(&offset, -96*FRACUNIT, 160*FRACUNIT); + FV2_Divide(&offset, scale); + for (i = -1; i < 2; i += 2) + { + P_SetTarget(&part->hprev, P_SpawnMobjFromMobj(base, + P_ReturnThrustX(NULL, base->angle - ANGLE_90, i*offset.x) + P_ReturnThrustX(NULL, base->angle, offset.y), + P_ReturnThrustY(NULL, base->angle - ANGLE_90, i*offset.x) + P_ReturnThrustY(NULL, base->angle, offset.y), + 0, MT_RINGSHOOTER_PART)); + P_SetTarget(&part->hprev->hnext, part); + part = part->hprev; + P_SetTarget(&part->target, base); + + P_InitAngle(part, base->angle - i * ANGLE_45); + P_SetMobjState(part, S_RINGSHOOTER_NIPPLES); + part->frame += frameNum; + frameNum++; + } + + // spawn the box + part = base; + frameNum = 0; + angle = base->angle + ANGLE_90; + FV2_Load(&offset, offset.x - info->radius, offset.y - info->radius); // set the new origin to the centerpoint of the box + FV2_Load(&offset, + P_ReturnThrustX(NULL, base->angle - ANGLE_90, offset.x) + P_ReturnThrustX(NULL, base->angle, offset.y), + P_ReturnThrustY(NULL, base->angle - ANGLE_90, offset.x) + P_ReturnThrustY(NULL, base->angle, offset.y)); // transform it relative to the base + for (i = 0; i < 4; i++) + { + P_SetTarget(&part->hnext, P_SpawnMobjFromMobj(base, + offset.x + P_ReturnThrustX(NULL, angle, info->radius), + offset.y + P_ReturnThrustY(NULL, angle, info->radius), + 0, MT_RINGSHOOTER_PART)); + P_SetTarget(&part->hnext->hprev, part); + part = part->hnext; + P_SetTarget(&part->target, base); + + if (i == 2) + frameNum++; + frameNum ^= FF_HORIZONTALFLIP; + angle -= ANGLE_90; + part->frame += frameNum; + P_InitAngle(part, angle); + } + + // spawn the screen + part = P_SpawnMobjFromMobj(base, offset.x, offset.y, info->height, MT_RINGSHOOTER_SCREEN); + P_SetTarget(&base->tracer, part); + P_SetTarget(&part->target, base); + P_InitAngle(part, base->angle - ANGLE_45); + + // spawn the screen numbers + for (i = 0; i < 2; i++) + { + P_SetTarget(&part->tracer, P_SpawnMobjFromMobj(part, 0, 0, 0, MT_OVERLAY)); + P_SetTarget(&part->tracer->target, part); + part = part->tracer; + P_InitAngle(part, part->target->angle); + P_SetMobjState(part, S_RINGSHOOTER_NUMBERBACK + i); + } + + // test face feature (to be moved into thinker later) + part->skin = mo->skin; + P_SetMobjState(part, S_RINGSHOOTER_FACE); +} diff --git a/src/k_kart.h b/src/k_kart.h index 782740ce8..0c91fe601 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -189,6 +189,7 @@ boolean K_IsSPBInGame(void); void K_KartEbrakeVisuals(player_t *p); void K_HandleDirectionalInfluence(player_t *player); fixed_t K_DefaultPlayerRadius(player_t *player); +void K_SpawnRingShooter(player_t *player); // sound stuff for lua void K_PlayAttackTaunt(mobj_t *source); diff --git a/src/p_enemy.c b/src/p_enemy.c index 9c457bdb5..3278e8249 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -330,6 +330,7 @@ void A_MementosTPParticles(mobj_t *actor); void A_FlameShieldPaper(mobj_t *actor); void A_InvincSparkleRotate(mobj_t *actor); void A_SpawnItemDebrisCloud(mobj_t *actor); +void A_RingShooterFace(mobj_t *actor); //for p_enemy.c @@ -13810,3 +13811,49 @@ A_SpawnItemDebrisCloud (mobj_t *actor) puff->momz += FixedMul(target->momz, fade); } } + +// sets the actor's +// vars do nothing +void A_RingShooterFace(mobj_t *actor) +{ + player_t *player; + mobj_t *mo = actor; + + if (LUA_CallAction(A_RINGSHOOTERFACE, actor)) + return; + + // get the player, if possible + while ((mo->player == NULL) && !P_MobjWasRemoved(mo->target)) + mo = mo->target; + + player = mo->player; + + if (!player) // something changed my target, abort + return; + + // it's a good idea to set the actor's skin *before* it uses this action, + // but just in case, if it doesn't have the player's skin, set its skin then call the state again to get the correct sprite + if (actor->skin != &skins[player->skin]) + { + actor->skin = &skins[player->skin]; + P_SetMobjState(actor, (statenum_t)(actor->state-states)); + return; + } + + // okay, now steal the player's color nyehehehe + actor->color = player->skincolor; + + // set the frame to the WANTED pic + actor->frame = (actor->frame & ~FF_FRAMEMASK) | FACE_WANTED; + + // we're going to assume the character's WANTED icon is 32 x 32 + // let's squish the sprite a bit so that it matches the dimensions of the screen's sprite, which is 26 x 22 + // (TODO: maybe get the dimensions/offsets from the patches themselves?) + actor->spritexscale = FixedDiv(26*FRACUNIT, 32*FRACUNIT); + actor->spriteyscale = FixedDiv(22*FRACUNIT, 32*FRACUNIT); + + // a normal WANTED icon should have (0, 0) offsets + // so let's offset it such that it will match the position of the screen's sprite + actor->spritexoffset = 16*FRACUNIT; // 32 / 2 + actor->spriteyoffset = 28*FRACUNIT + FixedDiv(11*FRACUNIT, actor->spriteyscale); // 32 - 4 (generic monster bottom) + 11 (vertical offset of screen sprite from the bottom) +} diff --git a/src/p_mobj.c b/src/p_mobj.c index 46f2443ae..376afcb06 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4550,6 +4550,25 @@ static void P_SpawnItemCapsuleParts(mobj_t *mobj) #undef ANG_CAPSULE #undef ROTATIONSPEED +static void P_RingShooterThinker(mobj_t *mo) +{ + UINT32 trans; + mobj_t *part = mo; + + while (!P_MobjWasRemoved(part->tracer)) + part = part->tracer; + + if (part == mo) // ??? where did you go + return; + + part->renderflags ^= RF_DONTDRAW; + if (part->renderflags & RF_DONTDRAW) + trans = FF_TRANS50; + else + trans = 0; + part->target->frame = (part->target->frame & ~FF_TRANSMASK) | trans; +} + // // P_BossTargetPlayer // If closest is true, find the closest player. @@ -6641,6 +6660,9 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->momz = newz - mobj->z; } break; + case MT_RINGSHOOTER: + P_RingShooterThinker(mobj); + break; case MT_SPINDASHWIND: case MT_DRIFTELECTRICSPARK: mobj->renderflags ^= RF_DONTDRAW;