diff --git a/src/dehacked.c b/src/dehacked.c index 8057f26d8..4673f2799 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6654,6 +6654,12 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_LOCKON1", "S_LOCKON2", + "S_LOCKON3", + "S_LOCKON4", + "S_LOCKONINF1", + "S_LOCKONINF2", + "S_LOCKONINF3", + "S_LOCKONINF4", // Tag Sign "S_TTAG", @@ -7732,6 +7738,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_DROWNNUMBERS", // Drowning Timer "MT_GOTEMERALD", // Chaos Emerald (intangible) "MT_LOCKON", // Target + "MT_LOCKONINF", // In-level Target "MT_TAG", // Tag Sign "MT_GOTFLAG", // Got Flag sign diff --git a/src/doomdef.h b/src/doomdef.h index 475328918..7bcc533c2 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -384,7 +384,7 @@ enum { LE_PINCHPHASE = -2, // A boss entered pinch phase (and, in most cases, is preparing their pinch phase attack!) LE_ALLBOSSESDEAD = -3, // All bosses in the map are dead (Egg capsule raise) LE_BOSSDEAD = -4, // A boss in the map died (Chaos mode boss tally) - LE_BOSS4DROP = -5, // CEZ boss dropped its cage + LE_BOSS4DROP = -5, // CEZ boss dropped its cage (also subtract the number of hitpoints it's lost) LE_BRAKVILEATACK = -6, // Brak's doing his LOS attack, oh noes LE_TURRET = 32000, // THZ turret LE_BRAKPLATFORM = 4200, // v2.0 Black Eggman destroys platform diff --git a/src/info.c b/src/info.c index 074e31ba2..5af1586b4 100644 --- a/src/info.c +++ b/src/info.c @@ -1352,14 +1352,15 @@ state_t states[NUMSTATES] = {SPR_EGGP,10, 2, {NULL}, 0, 0, S_EGGMOBILE4_STND}, // S_EGGMOBILE4_RATK6 {SPR_EGGP, 0, 20, {A_Boss4Raise}, sfx_doord1, 0, S_EGGMOBILE4_RAISE2}, // S_EGGMOBILE4_RAISE1 {SPR_EGGP,13, 10, {NULL}, 0, 0, S_EGGMOBILE4_RAISE3}, // S_EGGMOBILE4_RAISE2 - {SPR_EGGP,14, 10, {NULL}, 0, 0, S_EGGMOBILE4_RAISE4}, // S_EGGMOBILE4_RAISE3 + {SPR_EGGP,14, 10, {NULL}, 0, 0, S_EGGMOBILE4_RAISE2}, // S_EGGMOBILE4_RAISE3 {SPR_EGGP,13, 10, {NULL}, 0, 0, S_EGGMOBILE4_RAISE5}, // S_EGGMOBILE4_RAISE4 {SPR_EGGP,14, 10, {NULL}, 0, 0, S_EGGMOBILE4_RAISE6}, // S_EGGMOBILE4_RAISE5 {SPR_EGGP,13, 10, {NULL}, 0, 0, S_EGGMOBILE4_RAISE7}, // S_EGGMOBILE4_RAISE6 {SPR_EGGP,14, 10, {NULL}, 0, 0, S_EGGMOBILE4_RAISE8}, // S_EGGMOBILE4_RAISE7 {SPR_EGGP,13, 10, {NULL}, 0, 0, S_EGGMOBILE4_RAISE9}, // S_EGGMOBILE4_RAISE8 {SPR_EGGP,14, 10, {NULL}, 0, 0, S_EGGMOBILE4_RAISE10},// S_EGGMOBILE4_RAISE9 - {SPR_EGGP,13, 10, {NULL}, 0, 0, S_EGGMOBILE4_STND}, // S_EGGMOBILE4_RAISE10 + // rename eventually + {SPR_EGGP,11, 0, {A_Boss4Reverse}, sfx_mswing, 0, S_EGGMOBILE4_PAIN}, // S_EGGMOBILE4_RAISE10 {SPR_EGGP,11, 24, {A_Pain}, 0, 0, S_EGGMOBILE4_STND}, // S_EGGMOBILE4_PAIN {SPR_EGGP,12, 8, {A_Fall}, 0, 0, S_EGGMOBILE4_DIE2}, // S_EGGMOBILE4_DIE1 {SPR_EGGP,12, 8, {A_BossScream}, 0, 0, S_EGGMOBILE4_DIE3}, // S_EGGMOBILE4_DIE2 @@ -3298,6 +3299,13 @@ state_t states[NUMSTATES] = {SPR_LCKN, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON1 {SPR_LCKN, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON2 + {SPR_LCKN, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON3 + {SPR_LCKN, 3|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON4 + + {SPR_LCKN, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_LOCKONINF1 + {SPR_LCKN, 1|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_LOCKONINF2 + {SPR_LCKN, 2|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_LOCKONINF3 + {SPR_LCKN, 3|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_LOCKONINF4 {SPR_TTAG, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_TTAG @@ -5577,7 +5585,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // seesound 0, // reactiontime sfx_None, // attacksound - S_EGGMOBILE4_PAIN, // painstate + S_EGGMOBILE4_RAISE10, // painstate 0, // painchance sfx_dmpain, // painsound S_EGGMOBILE4_LATK1,// meleestate @@ -16529,6 +16537,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_LOCKONINF + 1126, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // 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 + 8, // speed + 16*FRACUNIT, // radius + 32*FRACUNIT, // height + 111, // display offset + 16, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags + S_NULL // raisestate + }, + { // MT_TAG -1, // doomednum S_TTAG, // spawnstate diff --git a/src/info.h b/src/info.h index 13abfa5f6..75091ce1e 100644 --- a/src/info.h +++ b/src/info.h @@ -3409,6 +3409,12 @@ typedef enum state S_LOCKON1, S_LOCKON2, + S_LOCKON3, + S_LOCKON4, + S_LOCKONINF1, + S_LOCKONINF2, + S_LOCKONINF3, + S_LOCKONINF4, // Tag Sign S_TTAG, @@ -4507,6 +4513,7 @@ typedef enum mobj_type MT_DROWNNUMBERS, // Drowning Timer MT_GOTEMERALD, // Chaos Emerald (intangible) MT_LOCKON, // Target + MT_LOCKONINF, // In-level Target MT_TAG, // Tag Sign MT_GOTFLAG, // Got Flag sign diff --git a/src/p_enemy.c b/src/p_enemy.c index a817ee8a6..05915c0b3 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3167,8 +3167,12 @@ void A_Boss4Reverse(mobj_t *actor) actor->reactiontime = 0; if (actor->movedir == 1) actor->movedir = 2; - else + else if (actor->movedir == 2) actor->movedir = 1; + else if (actor->movedir == 4) + actor->movedir = 5; + else + actor->movedir = 4; } // Function: A_Boss4SpeedUp diff --git a/src/p_mobj.c b/src/p_mobj.c index 4f46116ca..a3e0d7704 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4660,9 +4660,9 @@ static void P_Boss3Thinker(mobj_t *mobj) } // Move Boss4's sectors by delta. -static boolean P_Boss4MoveCage(fixed_t delta) +static boolean P_Boss4MoveCage(mobj_t *mobj, fixed_t delta) { - const UINT16 tag = 65534; + const UINT16 tag = 65534 + (mobj->spawnpoint ? mobj->spawnpoint->extrainfo*LE_PARAMWIDTH : 0); INT32 snum; sector_t *sector; for (snum = sectors[tag%numsectors].firsttag; snum != -1; snum = sector->nexttag) @@ -4682,7 +4682,7 @@ static void P_Boss4MoveSpikeballs(mobj_t *mobj, angle_t angle, fixed_t fz) { INT32 s; mobj_t *base = mobj, *seg; - fixed_t dist, bz = mobj->watertop+(16<watertop+(8<tracer)) { for (seg = base, dist = 172*FRACUNIT, s = 9; seg; seg = seg->hnext, dist += 124*FRACUNIT, --s) @@ -4692,26 +4692,44 @@ static void P_Boss4MoveSpikeballs(mobj_t *mobj, angle_t angle, fixed_t fz) } // Pull them closer. -static void P_Boss4PinchSpikeballs(mobj_t *mobj, angle_t angle, fixed_t fz) +static void P_Boss4PinchSpikeballs(mobj_t *mobj, angle_t angle, fixed_t dz) { INT32 s; mobj_t *base = mobj, *seg; - fixed_t dist, bz = mobj->watertop+(16<tracer)) + fixed_t originx, originy, workx, worky, dx, dy, bz = mobj->watertop+(8<spawnpoint) { - for (seg = base, dist = 112*FRACUNIT, s = 9; seg; seg = seg->hnext, dist += 132*FRACUNIT, --s) + originx = mobj->spawnpoint->x << FRACBITS; + originy = mobj->spawnpoint->y << FRACBITS; + } + else + { + originx = mobj->x; + originy = mobj->y; + } + + dz /= 9; + + while ((base = base->tracer)) // there are 10 per spoke, remember that + { + dx = (originx + P_ReturnThrustX(mobj, angle, (9*132)<x)/9; + dy = (originy + P_ReturnThrustY(mobj, angle, (9*132)<y)/9; + workx = mobj->x + P_ReturnThrustX(mobj, angle, (112)<y + P_ReturnThrustY(mobj, angle, (112)<hnext, --s) { - seg->z = bz + FixedMul(fz, FixedDiv(s<x + P_ReturnThrustX(mobj, angle, dist), mobj->y + P_ReturnThrustY(mobj, angle, dist), true); + seg->z = bz + (dz*(9-s)); + P_TryMove(seg, workx + (dx*s), worky + (dy*s), true); } angle += ANGLE_MAX/3; } } // Destroy cage FOFs. -static void P_Boss4DestroyCage(void) +static void P_Boss4DestroyCage(mobj_t *mobj) { - const UINT16 tag = 65534; + const UINT16 tag = 65534 + (mobj->spawnpoint ? mobj->spawnpoint->extrainfo*LE_PARAMWIDTH : 0); INT32 snum, next; size_t a; sector_t *sector, *rsec; @@ -4776,7 +4794,7 @@ static void P_Boss4Thinker(mobj_t *mobj) { if ((statenum_t)(mobj->state-states) == mobj->info->spawnstate) { - if (mobj->health > mobj->info->damage || mobj->movedir == 4) + if (mobj->flags2 & MF2_FRET && (mobj->health > mobj->info->damage || mobj->movedir == 4)) mobj->flags2 &= ~MF2_FRET; mobj->reactiontime = 0; // Drop the cage immediately. } @@ -4786,7 +4804,7 @@ static void P_Boss4Thinker(mobj_t *mobj) { if (mobj->tracer) // need to clean up! { - P_Boss4DestroyCage(); // Just in case pinch phase was skipped. + P_Boss4DestroyCage(mobj); // Just in case pinch phase was skipped. P_Boss4PopSpikeballs(mobj); } return; @@ -4813,6 +4831,7 @@ static void P_Boss4Thinker(mobj_t *mobj) mobj_t *seg, *base = mobj; // First frame init, spawn all the things. mobj->watertop = mobj->z; + mobj->flags2 |= MF2_STRONGBOX; // don't perform the linedef executor at start z = mobj->z + mobj->height/2 - mobjinfo[MT_EGGMOBILE4_MACE].height/2; for (arm = 0; arm <3 ; arm++) { @@ -4829,10 +4848,10 @@ static void P_Boss4Thinker(mobj_t *mobj) } // Move the cage up to the sky. mobj->movecount = 800*FRACUNIT; - if (!P_Boss4MoveCage(mobj->movecount)) + if (!P_Boss4MoveCage(mobj, mobj->movecount)) { mobj->movecount = 0; - mobj->threshold = 3*TICRATE; + //mobj->threshold = 3*TICRATE; mobj->extravalue1 = 1; mobj->movedir++; // We don't have a cage, just continue. } @@ -4846,13 +4865,18 @@ static void P_Boss4Thinker(mobj_t *mobj) mobj->movecount += mobj->threshold; if (mobj->movecount < 0) mobj->movecount = 0; - P_Boss4MoveCage(mobj->movecount - oldz); + P_Boss4MoveCage(mobj, mobj->movecount - oldz); P_Boss4MoveSpikeballs(mobj, 0, mobj->movecount); if (mobj->movecount == 0) { - mobj->threshold = 3*TICRATE; + //mobj->threshold = 3*TICRATE; mobj->extravalue1 = 1; - P_LinedefExecute(LE_BOSS4DROP, mobj, NULL); + //P_LinedefExecute(LE_BOSS4DROP + (mobj->spawnpoint ? mobj->spawnpoint->extrainfo*LE_PARAMWIDTH : 0), mobj, NULL); -- oh no you don't + S_StartSound(NULL, sfx_doorc2); + quake.intensity = 10<movedir++; // Initialization complete, next phase! } } @@ -4875,10 +4899,10 @@ static void P_Boss4Thinker(mobj_t *mobj) mobj->momz = 0; mobj->movedir++; } - mobj->movecount += 400<<(FRACBITS>>1); + mobj->movecount -= 210<<(FRACBITS>>1); mobj->movecount %= 360*FRACUNIT; z = mobj->z - mobj->watertop - mobjinfo[MT_EGGMOBILE4_MACE].height - mobj->height/2; - if (z < 0) // We haven't risen high enough to pull the spikeballs along yet + if (z < (8<movecount), 0); // So don't pull the spikeballs along yet. else P_Boss4PinchSpikeballs(mobj, FixedAngle(mobj->movecount), z); @@ -4886,12 +4910,19 @@ static void P_Boss4Thinker(mobj_t *mobj) } // Pinch phase! case 4: + case 5: { + fixed_t movespeed = 420<<(FRACBITS>>1); + movespeed += (420*(mobj->info->damage-mobj->health)<<(FRACBITS>>1)); + if (mobj->movedir == 4) + mobj->movecount -= movespeed; + else + mobj->movecount += movespeed; + if (mobj->z < (mobj->watertop + ((512+128*(mobj->info->damage-mobj->health))<momz = 8*FRACUNIT; else mobj->momz = 0; - mobj->movecount += (800+800*(mobj->info->damage-mobj->health))<<(FRACBITS>>1); mobj->movecount %= 360*FRACUNIT; P_Boss4PinchSpikeballs(mobj, FixedAngle(mobj->movecount), mobj->z - mobj->watertop - mobjinfo[MT_EGGMOBILE4_MACE].height - mobj->height/2); @@ -4917,10 +4948,20 @@ static void P_Boss4Thinker(mobj_t *mobj) if (mobj->reactiontime == 1) { fixed_t oldz = mobj->movefactor; - mobj->movefactor += 8*FRACUNIT; - if (mobj->movefactor > 128*FRACUNIT) + if (mobj->movefactor < 128*FRACUNIT) + { + mobj->movefactor += 8*FRACUNIT; + P_Boss4MoveCage(mobj, mobj->movefactor - oldz); + // 5 -> 2.5 second timer + mobj->threshold = 5*TICRATE-(TICRATE/2)*(mobj->info->spawnhealth-mobj->health); + if (mobj->threshold < 1) + mobj->threshold = 1; + } + else if (mobj->movefactor > 128*FRACUNIT) + { mobj->movefactor = 128*FRACUNIT; - P_Boss4MoveCage(mobj->movefactor - oldz); + P_Boss4MoveCage(mobj, mobj->movefactor - oldz); + } } // Drop the cage! else if (mobj->movefactor) @@ -4929,24 +4970,32 @@ static void P_Boss4Thinker(mobj_t *mobj) mobj->movefactor -= 4*FRACUNIT; if (mobj->movefactor < 0) mobj->movefactor = 0; - P_Boss4MoveCage(mobj->movefactor - oldz); - if (!mobj->movefactor) + P_Boss4MoveCage(mobj, mobj->movefactor - oldz); + if (mobj->flags2 & MF2_STRONGBOX) + mobj->flags2 &= ~MF2_STRONGBOX; + else if (!mobj->movefactor) { if (mobj->health <= mobj->info->damage) { // Proceed to pinch phase! - P_Boss4DestroyCage(); + P_Boss4DestroyCage(mobj); mobj->movedir = 3; - P_LinedefExecute(LE_PINCHPHASE, mobj, NULL); + P_LinedefExecute(LE_PINCHPHASE + (mobj->spawnpoint ? mobj->spawnpoint->extrainfo*LE_PARAMWIDTH : 0), mobj, NULL); return; } - P_LinedefExecute(LE_BOSS4DROP, mobj, NULL); + P_LinedefExecute(LE_BOSS4DROP - (mobj->info->spawnhealth-mobj->health) + (mobj->spawnpoint ? mobj->spawnpoint->extrainfo*LE_PARAMWIDTH : 0), mobj, NULL); + S_StartSound(NULL, sfx_doorc2); + quake.intensity = 10<>1); - if (mobj->reactiontime == 2) - movespeed *= 3; + movespeed += ((50*(mobj->info->spawnhealth-mobj->health))<<(FRACBITS>>1)); + if (mobj->movefactor) + movespeed /= 2; if (mobj->movedir == 2) mobj->movecount -= movespeed; else @@ -4956,37 +5005,14 @@ static void P_Boss4Thinker(mobj_t *mobj) P_Boss4MoveSpikeballs(mobj, FixedAngle(mobj->movecount), mobj->movefactor); // Check for attacks, always tick the timer even while animating!! - if (!(mobj->flags2 & MF2_FRET) // but pause for pain so we don't interrupt pinch phase, eep! - && mobj->threshold-- == 0) + if (mobj->threshold) { - // 5 -> 2.5 second timer - mobj->threshold = 5*TICRATE-(TICRATE/2)*(mobj->info->spawnhealth-mobj->health); - if (mobj->threshold < 1) - mobj->threshold = 1; - - if (mobj->extravalue1-- == 0) - { - P_SetMobjState(mobj, mobj->info->raisestate); - mobj->extravalue1 = 3; - } - else + if (!(mobj->flags2 & MF2_FRET) && !(--mobj->threshold)) // but pause for pain so we don't interrupt pinch phase, eep! { if (mobj->reactiontime == 1) // Cage is raised? - mobj->reactiontime = 0; // Drop it! - switch(P_RandomKey(10)) { - // Telegraph Right (Speed Up!!) - case 1: - case 3: - case 4: - case 5: - case 6: - P_SetMobjState(mobj, mobj->info->missilestate); - break; - // Telegraph Left (Reverse Direction) - default: - P_SetMobjState(mobj, mobj->info->meleestate); - break; + P_SetMobjState(mobj, mobj->info->spawnstate); + mobj->reactiontime = 0; // Drop it! } } } @@ -4998,13 +5024,13 @@ static void P_Boss4Thinker(mobj_t *mobj) // Map allows us to get killed despite cage being down? if (mobj->health <= mobj->info->damage) { // Proceed to pinch phase! - P_Boss4DestroyCage(); + P_Boss4DestroyCage(mobj); // spawn jet's flame now you're flying upwards // tracer is already used, so if this ever gets reached again we've got problems var1 = 3; A_BossJetFume(mobj); mobj->movedir = 3; - P_LinedefExecute(LE_PINCHPHASE, mobj, NULL); + P_LinedefExecute(LE_PINCHPHASE + (mobj->spawnpoint ? mobj->spawnpoint->extrainfo*LE_PARAMWIDTH : 0), mobj, NULL); return; } @@ -7183,6 +7209,17 @@ void P_MobjThinker(mobj_t *mobj) else mobj->z = mobj->target->z - FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->target->scale) - mobj->height; break; + case MT_LOCKONINF: + if (!(mobj->flags2 & MF2_STRONGBOX)) + { + mobj->threshold = mobj->z; + mobj->flags2 |= MF2_STRONGBOX; + } + if (!(mobj->eflags & MFE_VERTICALFLIP)) + mobj->z = mobj->threshold + FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->scale); + else + mobj->z = mobj->threshold - FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->scale); + break; case MT_DROWNNUMBERS: if (!mobj->target) { @@ -9184,6 +9221,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_ALTVIEWMAN: if (titlemapinaction) mobj->flags &= ~MF_NOTHINK; break; + case MT_LOCKONINF: + P_SetScale(mobj, (mobj->destscale = 3*mobj->scale)); + break; case MT_CYBRAKDEMON_NAPALM_BOMB_LARGE: mobj->fuse = mobj->info->painchance; break;