diff --git a/src/dehacked.c b/src/dehacked.c index e98fbcdf2..6d1f58990 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2381,6 +2381,18 @@ static actionpointer_t actionpointers[] = {{A_ParentTriesToSleep}, "A_PARENTTRIESTOSLEEP"}, {{A_CryingToMomma}, "A_CRYINGTOMOMMA"}, {{A_CheckFlags2}, "A_CHECKFLAGS2"}, + {{A_Boss5FindWaypoint}, "A_BOSS5FINDWAYPOINT"}, + {{A_DoNPCSkid}, "A_DONPCSKID"}, + {{A_DoNPCPain}, "A_DONPCPAIN"}, + {{A_PrepareRepeat}, "A_PREPAREREPEAT"}, + {{A_Boss5ExtraRepeat}, "A_BOSS5EXTRAREPEAT"}, + {{A_Boss5Calm}, "A_BOSS5CALM"}, + {{A_Boss5CheckOnGround}, "A_BOSS5CHECKONGROUND"}, + {{A_Boss5CheckFalling}, "A_BOSS5CHECKFALLING"}, + {{A_Boss5PinchShot}, "A_BOSS5PINCHSHOT"}, + {{A_Boss5MakeItRain}, "A_BOSS5MAKEITRAIN"}, + {{A_LookForBetter}, "A_LOOKFORBETTER"}, + {{A_Boss5BombExplode}, "A_BOSS5BOMBEXPLODE"}, {{NULL}, "NONE"}, @@ -4704,6 +4716,96 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_JETFLAME1", "S_JETFLAME2", + // Boss 5 + "S_FANG_IDLE1", + "S_FANG_IDLE2", + "S_FANG_IDLE3", + "S_FANG_IDLE4", + "S_FANG_IDLE5", + "S_FANG_IDLE6", + "S_FANG_IDLE7", + "S_FANG_IDLE8", + "S_FANG_PAIN1", + "S_FANG_PAIN2", + "S_FANG_PATHINGSTART1", + "S_FANG_PATHINGSTART2", + "S_FANG_PATHING", + "S_FANG_BOUNCE1", + "S_FANG_BOUNCE2", + "S_FANG_BOUNCE3", + "S_FANG_BOUNCE4", + "S_FANG_FALL1", + "S_FANG_FALL2", + "S_FANG_CHECKPATH1", + "S_FANG_CHECKPATH2", + "S_FANG_PATHINGCONT1", + "S_FANG_PATHINGCONT2", + "S_FANG_PATHINGCONT3", + "S_FANG_SKID1", + "S_FANG_SKID2", + "S_FANG_SKID3", + "S_FANG_CHOOSEATTACK", + "S_FANG_FIRESTART1", + "S_FANG_FIRESTART2", + "S_FANG_FIRE1", + "S_FANG_FIRE2", + "S_FANG_FIRE3", + "S_FANG_FIRE4", + "S_FANG_FIREREPEAT", + "S_FANG_LOBSHOT1", + "S_FANG_LOBSHOT2", + "S_FANG_WAIT1", + "S_FANG_WAIT2", + "S_FANG_WALLHIT", + "S_FANG_PINCHPATHINGSTART1", + "S_FANG_PINCHPATHINGSTART2", + "S_FANG_PINCHPATHING", + "S_FANG_PINCHBOUNCE1", + "S_FANG_PINCHBOUNCE2", + "S_FANG_PINCHBOUNCE3", + "S_FANG_PINCHBOUNCE4", + "S_FANG_PINCHFALL1", + "S_FANG_PINCHFALL2", + "S_FANG_PINCHSKID1", + "S_FANG_PINCHSKID2", + "S_FANG_PINCHLOBSHOT1", + "S_FANG_PINCHLOBSHOT2", + "S_FANG_PINCHLOBSHOT3", + "S_FANG_PINCHLOBSHOT4", + "S_FANG_DIE1", + "S_FANG_DIE2", + "S_FANG_DIE3", + "S_FANG_DIE4", + "S_FANG_DIE5", + "S_FANG_DIE6", + "S_FANG_DIE7", + "S_FANG_DIE8", + "S_FANG_FLEEPATHING1", + "S_FANG_FLEEPATHING2", + "S_FANG_FLEEBOUNCE1", + "S_FANG_FLEEBOUNCE2", + "S_FANG_KO", + + "S_FBOMB1", + "S_FBOMB2", + "S_FBOMB_EXPL1", + "S_FBOMB_EXPL2", + "S_FBOMB_EXPL3", + "S_FBOMB_EXPL4", + "S_FBOMB_EXPL5", + "S_FBOMB_EXPL6", + "S_TNTDUST_1", + "S_TNTDUST_2", + "S_TNTDUST_3", + "S_TNTDUST_4", + "S_TNTDUST_5", + "S_TNTDUST_6", + "S_TNTDUST_7", + "S_TNTDUST_8", + "S_FSGNA", + "S_FSGNB", + "S_FSGNC", + // Black Eggman (Boss 7) "S_BLACKEGG_STND", "S_BLACKEGG_STND2", @@ -7004,6 +7106,14 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_EGGMOBILE4_MACE", "MT_JETFLAME", + // Boss 5 + "MT_FANG", + "MT_FBOMB", + "MT_TNTDUST", // also used by barrel + "MT_FSGNA", + "MT_FSGNB", + "MT_FANGWAYPOINT", + // Black Eggman (Boss 7) "MT_BLACKEGGMAN", "MT_BLACKEGGMAN_HELPER", diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index 2515c6a86..90d02fdf8 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -199,7 +199,11 @@ light_t *t_lspr[NUMSPRITES] = &lspr[REDBALL_L], // SPR_EFIR // Boss 5 (Arid Canyon) - &lspr[NOLIGHT], // SPR_EGGQ + &lspr[NOLIGHT], //SPR_FANG // replaces EGGQ + &lspr[NOLIGHT], //SPR_FBOM + &lspr[NOLIGHT], //SPR_FSGN + &lspr[REDBALL_L], //SPR_BARX // bomb explosion (also used by barrel) + &lspr[NOLIGHT], //SPR_BARD // bomb dust (also used by barrel) // Boss 6 (Red Volcano) &lspr[NOLIGHT], // SPR_EEGR diff --git a/src/info.c b/src/info.c index 89ce48559..e03b63a65 100644 --- a/src/info.c +++ b/src/info.c @@ -87,7 +87,11 @@ char sprnames[NUMSPRITES + 1][5] = "EFIR", // Boss 4 jet flame // Boss 5 (Arid Canyon) - "EGGQ", + "FANG", // replaces EGGQ + "FBOM", + "FSGN", + "BARX", // bomb explosion (also used by barrel) + "BARD", // bomb dust (also used by barrel) // Boss 6 (Red Volcano) "EGGR", @@ -1318,6 +1322,115 @@ state_t states[NUMSTATES] = {SPR_EFIR, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_JETFLAME2}, // S_JETFLAME1 {SPR_EFIR, FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_JETFLAME1}, // S_JETFLAME2 + // Boss 5 + {SPR_FANG, 2, 16, {A_Look}, 1, 0, S_FANG_IDLE2}, // S_FANG_IDLE1 + {SPR_FANG, 3, 16, {A_Look}, 1, 0, S_FANG_IDLE3}, // S_FANG_IDLE2 + {SPR_FANG, 3, 16, {A_Look}, 1, 0, S_FANG_IDLE4}, // S_FANG_IDLE3 + {SPR_FANG, 3, 16, {A_Look}, 1, 0, S_FANG_IDLE5}, // S_FANG_IDLE4 + {SPR_FANG, 2, 16, {A_Look}, 1, 0, S_FANG_IDLE6}, // S_FANG_IDLE5 + {SPR_FANG, 1, 16, {A_Look}, 1, 0, S_FANG_IDLE7}, // S_FANG_IDLE6 + {SPR_FANG, 1, 16, {A_Look}, 1, 0, S_FANG_IDLE8}, // S_FANG_IDLE7 + {SPR_FANG, 1, 16, {A_Look}, 1, 0, S_FANG_IDLE1}, // S_FANG_IDLE8 + + {SPR_FANG, 14, 0, {A_DoNPCPain}, FRACUNIT, 0, S_FANG_PAIN2}, // S_FANG_PAIN1 + {SPR_FANG, 14, 1, {A_Boss5CheckOnGround}, S_FANG_PATHINGSTART1, S_FANG_PINCHPATHINGSTART1, S_FANG_PAIN2}, // S_FANG_PAIN2 + + {SPR_FANG, 8, 0, {A_Boss5ExtraRepeat}, 5, 4, S_FANG_PATHINGSTART2}, // S_FANG_PATHINGSTART1 + {SPR_FANG, 8, 0, {A_PlayActiveSound}, 0, 0, S_FANG_PATHING}, // S_FANG_PATHINGSTART2 + {SPR_FANG, 8, 0, {A_Boss5FindWaypoint}, 0, 0, S_FANG_BOUNCE1}, // S_FANG_PATHING + + {SPR_FANG, 8, 2, {A_Thrust}, 0, 1, S_FANG_BOUNCE2}, // S_FANG_BOUNCE1 + {SPR_FANG, 9, 2, {NULL}, 0, 0, S_FANG_BOUNCE3}, // S_FANG_BOUNCE2 + {SPR_FANG, 10, 1, {A_Boss5Jump}, 0, 0, S_FANG_BOUNCE4}, // S_FANG_BOUNCE3 + {SPR_FANG, 10, 1, {A_Boss5CheckFalling}, S_FANG_CHECKPATH1, S_FANG_FALL1, S_FANG_BOUNCE4}, // S_FANG_BOUNCE4 + + {SPR_FANG, 12, 1, {A_Boss5CheckOnGround}, S_FANG_CHECKPATH1, 0, S_FANG_FALL2}, // S_FANG_FALL1 + {SPR_FANG, 13, 1, {A_Boss5CheckOnGround}, S_FANG_CHECKPATH1, 0, S_FANG_FALL1}, // S_FANG_FALL2 + + {SPR_FANG, 8, 0, {A_Boss5Calm}, 0, 0, S_FANG_CHECKPATH2}, // S_FANG_CHECKPATH1 + {SPR_FANG, 8, 0, {A_Repeat}, 0, S_FANG_PATHINGCONT1, S_FANG_SKID1}, // S_FANG_CHECKPATH2 + + {SPR_FANG, 9, 0, {A_Boss5PinchShot}, MT_FBOMB, -16, S_FANG_PATHINGCONT2}, // S_FANG_PATHINGCONT1 + {SPR_FANG, 9, 0, {A_PlayActiveSound}, 0, 0, S_FANG_PATHINGCONT3}, // S_FANG_PATHINGCONT2 + {SPR_FANG, 9, 2, {A_Thrust}, 0, 1, S_FANG_PATHING}, // S_FANG_PATHINGCONT3 + + {SPR_FANG, 4, 0, {A_PlayAttackSound}, 0, 0, S_FANG_SKID2}, // S_FANG_SKID1 + {SPR_FANG, 4, 1, {A_DoNPCSkid}, S_FANG_SKID3, 0, S_FANG_SKID2}, // S_FANG_SKID2 + {SPR_FANG, 4, 10, {NULL}, 0, 0, S_FANG_CHOOSEATTACK}, // S_FANG_SKID3 + + {SPR_FANG, 0, 0, {A_RandomState}, S_FANG_LOBSHOT1, S_FANG_FIRESTART1, S_NULL}, // S_FANG_CHOOSEATTACK + + {SPR_FANG, 5, 0, {A_PrepareRepeat}, 3, 0, S_FANG_FIRESTART2}, // S_FANG_FIRESTART1 // Reset loop + {SPR_FANG, 5, 18, {A_LookForBetter}, 1, 0, S_FANG_FIRE1}, // S_FANG_FIRESTART2 + {SPR_FANG, 5, 5, {A_FireShot}, MT_CORK, -16, S_FANG_FIRE2}, // S_FANG_FIRE1 // Start of loop + {SPR_FANG, 6, 5, {NULL}, 0, 0, S_FANG_FIRE3}, // S_FANG_FIRE2 + {SPR_FANG, 7, 5, {NULL}, 0, 0, S_FANG_FIRE4}, // S_FANG_FIRE3 + {SPR_FANG, 5, 5, {NULL}, 2, 0, S_FANG_FIREREPEAT}, // S_FANG_FIRE4 + {SPR_FANG, 5, 0, {A_Repeat}, 3, S_FANG_FIRE1, S_FANG_WAIT1}, // S_FANG_FIREREPEAT // End of loop + + {SPR_FANG, 19, 18, {A_LookForBetter}, 1, 0, S_FANG_LOBSHOT2}, // S_FANG_LOBSHOT1 + {SPR_FANG, 20, 18, {A_BrakLobShot}, MT_FBOMB, 32+(1<<16), S_FANG_WAIT1}, // S_FANG_LOBSHOT2 + + {SPR_FANG, FF_ANIMATE|15, 70, {NULL}, 1, 5, S_FANG_WAIT2}, // S_FANG_WAIT1 + {SPR_FANG, 0, 35, {A_Look}, 1, 0, S_FANG_IDLE1}, // S_FANG_WAIT2 + + {SPR_FANG, 12, 1, {A_Boss5CheckOnGround}, S_FANG_PATHINGSTART2, S_FANG_PINCHPATHINGSTART1, S_FANG_WALLHIT}, // S_FANG_WALLHIT + + {SPR_FANG, 8, 0, {A_PrepareRepeat}, 1, 0, S_FANG_PINCHPATHINGSTART2}, // S_FANG_PINCHPATHINGSTART1 + {SPR_FANG, 8, 0, {A_PlayActiveSound}, 0, 0, S_FANG_PINCHPATHING}, // S_FANG_PINCHPATHINGSTART2 + {SPR_FANG, 8, 0, {A_Boss5FindWaypoint}, 1, 0, S_FANG_PINCHBOUNCE1}, // S_FANG_PINCHPATHING + {SPR_FANG, 8, 2, {A_Thrust}, 0, 1, S_FANG_PINCHBOUNCE2}, // S_FANG_PINCHBOUNCE1 + {SPR_FANG, 9, 2, {NULL}, 0, 0, S_FANG_PINCHBOUNCE3}, // S_FANG_PINCHBOUNCE2 + {SPR_FANG, 10, 2, {A_Boss5Jump}, 0, 0, S_FANG_PINCHBOUNCE4}, // S_FANG_PINCHBOUNCE3 + {SPR_FANG, 10, 1, {A_Boss5CheckFalling}, S_FANG_PINCHSKID1, S_FANG_PINCHFALL1, S_FANG_PINCHBOUNCE4}, // S_FANG_PINCHBOUNCE4 + {SPR_FANG, 12, 1, {A_Boss5CheckOnGround}, S_FANG_PINCHSKID1, 0, S_FANG_PINCHFALL2}, // S_FANG_PINCHFALL1 + {SPR_FANG, 13, 1, {A_Boss5CheckOnGround}, S_FANG_PINCHSKID1, 0, S_FANG_PINCHFALL1}, // S_FANG_PINCHFALL2 + {SPR_FANG, 4, 0, {A_PlayAttackSound}, 0, 0, S_FANG_PINCHSKID2}, // S_FANG_PINCHSKID1 + {SPR_FANG, 4, 1, {A_DoNPCSkid}, S_FANG_PINCHLOBSHOT1, 0, S_FANG_PINCHSKID2}, // S_FANG_PINCHSKID2 + {SPR_FANG, 19, 18, {A_FaceTarget}, 3, 0, S_FANG_PINCHLOBSHOT2}, // S_FANG_PINCHLOBSHOT1 + {SPR_FANG, 20, 30, {A_Boss5MakeItRain}, MT_FBOMB, -16, S_FANG_PINCHLOBSHOT3}, // S_FANG_PINCHLOBSHOT2 + {SPR_FANG, 19, 18, {A_LinedefExecute}, LE_BOSS4DROP, 0, S_FANG_PINCHLOBSHOT4}, // S_FANG_PINCHLOBSHOT3 + {SPR_FANG, 19, 0, {A_Boss5Calm}, 0, 0, S_FANG_PATHINGSTART1}, // S_FANG_PINCHLOBSHOT4 + + {SPR_FANG, 14, 0, {A_DoNPCPain}, 0, 0, S_FANG_DIE2}, // S_FANG_DIE1 + {SPR_FANG, 14, 1, {A_Boss5CheckOnGround}, S_FANG_DIE3, 0, S_FANG_DIE2}, // S_FANG_DIE2 + + {SPR_FANG, 17, 0, {A_Scream}, 0, 0, S_FANG_DIE4}, // S_FANG_DIE3 + {SPR_FANG, 17, 104, {NULL}, 0, 0, S_FANG_DIE5}, // S_FANG_DIE4 + + {SPR_FANG, 11, 0, {A_PlaySound}, sfx_jump, 0, S_FANG_DIE6}, // S_FANG_DIE5 + {SPR_FANG, 11, 1, {A_ZThrust}, 6, (1<<16)|1, S_FANG_DIE7}, // S_FANG_DIE6 + {SPR_FANG, 11, 1, {A_Boss5CheckFalling}, S_FANG_FLEEPATHING1, S_FANG_DIE8, S_FANG_DIE7}, // S_FANG_DIE7 + {SPR_FANG, 12, 1, {A_Boss5CheckOnGround}, S_FANG_FLEEPATHING1, 0, S_FANG_DIE8}, // S_FANG_DIE8 + + {SPR_FANG, 9, 0, {A_PlayActiveSound}, 0, 0, S_FANG_FLEEPATHING2}, // S_FANG_FLEEPATHING1 + {SPR_FANG, 8, 2, {A_Boss5FindWaypoint}, 2, 0, S_FANG_FLEEBOUNCE1}, // S_FANG_FLEEPATHING2 + {SPR_FANG, 9, 2, {NULL}, 0, 0, S_FANG_FLEEBOUNCE2}, // S_FANG_FLEEBOUNCE1 + {SPR_FANG, 10, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_FANG_FLEEBOUNCE2 + + {SPR_FANG, 18, 7*TICRATE, {NULL}, 0, 0, S_NULL}, // S_FANG_KO + + {SPR_FBOM, 0, 1, {A_GhostMe}, 0, 0, S_FBOMB2}, // S_FBOMB1 + {SPR_FBOM, 1, 1, {A_GhostMe}, 0, 0, S_FBOMB1}, // S_FBOMB2 + {SPR_BARX, 0|FF_FULLBRIGHT, 3, {A_SetObjectFlags}, MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP, 0, S_FBOMB_EXPL2}, // S_FBOMB_EXPL1 + {SPR_BARX, 1|FF_FULLBRIGHT, 2, {A_Boss5BombExplode}, MT_TNTDUST, 0, S_FBOMB_EXPL3}, // S_FBOMB_EXPL2 + {SPR_BARX, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_FBOMB_EXPL4}, // S_FBOMB_EXPL3 + {SPR_BARX, 2|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_FBOMB_EXPL5}, // S_FBOMB_EXPL4 + {SPR_BARX, 3|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_FBOMB_EXPL6}, // S_FBOMB_EXPL5 + {SPR_NULL, 0, 2*TICRATE, {NULL}, 0, 0, S_NULL}, // S_FBOMB_EXPL6 + {SPR_BARD, 0|FF_TRANS90, 2, {NULL}, 0, 0, S_TNTDUST_2}, // S_TNTDUST_1 + {SPR_BARD, 0|FF_TRANS30, 2*TICRATE, {A_SetRandomTics}, 2, TICRATE, S_TNTDUST_3}, // S_TNTDUST_2 + {SPR_BARD, 0|FF_TRANS40, 10, {NULL}, 0, 0, S_TNTDUST_4}, // S_TNTDUST_3 + {SPR_BARD, 0|FF_TRANS50, 10, {NULL}, 0, 0, S_TNTDUST_5}, // S_TNTDUST_4 + {SPR_BARD, 0|FF_TRANS60, 10, {NULL}, 0, 0, S_TNTDUST_6}, // S_TNTDUST_5 + {SPR_BARD, 0|FF_TRANS70, 10, {NULL}, 0, 0, S_TNTDUST_7}, // S_TNTDUST_6 + {SPR_BARD, 0|FF_TRANS80, 10, {NULL}, 0, 0, S_TNTDUST_8}, // S_TNTDUST_7 + {SPR_BARD, 0|FF_TRANS90, 10, {NULL}, 0, 0, S_NULL}, // S_TNTDUST_8 + {SPR_FSGN, 0|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGNA + {SPR_FSGN, 1|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGNB + {SPR_FSGN, 2|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGNC + + // Black Eggman (Boss 7) {SPR_BRAK, 0, 1, {A_SetReactionTime}, 0, 0, S_BLACKEGG_STND2}, // S_BLACKEGG_STND {SPR_BRAK, 0, 7, {A_Look}, 1, 0, S_BLACKEGG_STND2}, // S_BLACKEGG_STND2 {SPR_BRAK, 1, 7, {NULL}, 0, 0, S_BLACKEGG_WALK2}, // S_BLACKEGG_WALK1 @@ -5261,6 +5374,167 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_FANG + 204, // doomednum + S_FANG_IDLE1, // spawnstate + 8, // spawnhealth + S_FANG_PATHINGSTART1, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_skid, // attacksound + S_FANG_PAIN1, // painstate + 0, // painchance + sfx_s3k5d, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_FANG_DIE1, // deathstate + S_FANG_KO, // xdeathstate + sfx_s3k90, // deathsound + 0, // speed + 24*FRACUNIT, // radius + 60*FRACUNIT, // height + 0, // display offset + 0, // mass + 3, // damage + sfx_boingf, // activesound + MF_SPECIAL|MF_BOSS|MF_SHOOTABLE, // flags + S_NULL // raisestate + }, + + { // MT_FBOMB + -1, // doomednum + S_FBOMB1, // spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_s3k51, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_FBOMB_EXPL1, // deathstate + S_NULL, // xdeathstate + sfx_s3k4e, // deathsound + 20*FRACUNIT, // speed + 24*FRACUNIT, // radius + 48*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_s3k8d, // activesound + MF_NOBLOCKMAP|MF_MISSILE, // flags + S_NULL // raisestate + }, + + { // MT_TNTDUST + -1, // doomednum + S_TNTDUST_1, // 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_None, // deathsound + 20*FRACUNIT, // speed + 16*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + S_NULL // raisestate + }, + { // MT_FSGNA + -1, // doomednum + S_FSGNA, // 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_mspogo, // deathsound + 0, // speed + 124*FRACUNIT, // radius + 124*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_FSGNB + -1, // doomednum + S_FSGNB, // 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_FSGNC, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 124*FRACUNIT, // radius + 640*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY|MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_FANGWAYPOINT + 294, // 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 + FRACUNIT, // radius + FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOTHINK, // flags + S_NULL // raisestate + }, + { // MT_BLACKEGGMAN 206, // doomednum S_BLACKEGG_STND, // spawnstate diff --git a/src/info.h b/src/info.h index b63dae67c..fbe8ed8d7 100644 --- a/src/info.h +++ b/src/info.h @@ -239,6 +239,18 @@ void A_WhoCaresIfYourSonIsABee(); void A_ParentTriesToSleep(); void A_CryingToMomma(); void A_CheckFlags2(); +void A_Boss5FindWaypoint(); +void A_DoNPCSkid(); +void A_DoNPCPain(); +void A_PrepareRepeat(); +void A_Boss5ExtraRepeat(); +void A_Boss5Calm(); +void A_Boss5CheckOnGround(); +void A_Boss5CheckFalling(); +void A_Boss5PinchShot(); +void A_Boss5MakeItRain(); +void A_LookForBetter(); +void A_Boss5BombExplode(); // ratio of states to sprites to mobj types is roughly 6 : 1 : 1 #define NUMMOBJFREESLOTS 256 @@ -306,7 +318,11 @@ typedef enum sprite SPR_EFIR, // Boss 4 jet flame // Boss 5 (Arid Canyon) - SPR_EGGQ, + SPR_FANG, // replaces EGGQ + SPR_FBOM, + SPR_FSGN, + SPR_BARX, // bomb explosion (also used by barrel) + SPR_BARD, // bomb dust (also used by barrel) // Boss 6 (Red Volcano) SPR_EGGR, @@ -1445,6 +1461,96 @@ typedef enum state S_JETFLAME1, S_JETFLAME2, + // Boss 5 + S_FANG_IDLE1, + S_FANG_IDLE2, + S_FANG_IDLE3, + S_FANG_IDLE4, + S_FANG_IDLE5, + S_FANG_IDLE6, + S_FANG_IDLE7, + S_FANG_IDLE8, + S_FANG_PAIN1, + S_FANG_PAIN2, + S_FANG_PATHINGSTART1, + S_FANG_PATHINGSTART2, + S_FANG_PATHING, + S_FANG_BOUNCE1, + S_FANG_BOUNCE2, + S_FANG_BOUNCE3, + S_FANG_BOUNCE4, + S_FANG_FALL1, + S_FANG_FALL2, + S_FANG_CHECKPATH1, + S_FANG_CHECKPATH2, + S_FANG_PATHINGCONT1, + S_FANG_PATHINGCONT2, + S_FANG_PATHINGCONT3, + S_FANG_SKID1, + S_FANG_SKID2, + S_FANG_SKID3, + S_FANG_CHOOSEATTACK, + S_FANG_FIRESTART1, + S_FANG_FIRESTART2, + S_FANG_FIRE1, + S_FANG_FIRE2, + S_FANG_FIRE3, + S_FANG_FIRE4, + S_FANG_FIREREPEAT, + S_FANG_LOBSHOT1, + S_FANG_LOBSHOT2, + S_FANG_WAIT1, + S_FANG_WAIT2, + S_FANG_WALLHIT, + S_FANG_PINCHPATHINGSTART1, + S_FANG_PINCHPATHINGSTART2, + S_FANG_PINCHPATHING, + S_FANG_PINCHBOUNCE1, + S_FANG_PINCHBOUNCE2, + S_FANG_PINCHBOUNCE3, + S_FANG_PINCHBOUNCE4, + S_FANG_PINCHFALL1, + S_FANG_PINCHFALL2, + S_FANG_PINCHSKID1, + S_FANG_PINCHSKID2, + S_FANG_PINCHLOBSHOT1, + S_FANG_PINCHLOBSHOT2, + S_FANG_PINCHLOBSHOT3, + S_FANG_PINCHLOBSHOT4, + S_FANG_DIE1, + S_FANG_DIE2, + S_FANG_DIE3, + S_FANG_DIE4, + S_FANG_DIE5, + S_FANG_DIE6, + S_FANG_DIE7, + S_FANG_DIE8, + S_FANG_FLEEPATHING1, + S_FANG_FLEEPATHING2, + S_FANG_FLEEBOUNCE1, + S_FANG_FLEEBOUNCE2, + S_FANG_KO, + + S_FBOMB1, + S_FBOMB2, + S_FBOMB_EXPL1, + S_FBOMB_EXPL2, + S_FBOMB_EXPL3, + S_FBOMB_EXPL4, + S_FBOMB_EXPL5, + S_FBOMB_EXPL6, + S_TNTDUST_1, + S_TNTDUST_2, + S_TNTDUST_3, + S_TNTDUST_4, + S_TNTDUST_5, + S_TNTDUST_6, + S_TNTDUST_7, + S_TNTDUST_8, + S_FSGNA, + S_FSGNB, + S_FSGNC, + // Black Eggman (Boss 7) S_BLACKEGG_STND, S_BLACKEGG_STND2, @@ -3765,6 +3871,14 @@ typedef enum mobj_type MT_EGGMOBILE4_MACE, MT_JETFLAME, + // Boss 5 + MT_FANG, + MT_FBOMB, + MT_TNTDUST, // also used by barrel + MT_FSGNA, + MT_FSGNB, + MT_FANGWAYPOINT, + // Black Eggman (Boss 7) MT_BLACKEGGMAN, MT_BLACKEGGMAN_HELPER, diff --git a/src/p_enemy.c b/src/p_enemy.c index 9d2425e53..664e3b9b0 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -15,6 +15,7 @@ #include "doomdef.h" #include "g_game.h" #include "p_local.h" +#include "p_setup.h" #include "r_main.h" #include "r_state.h" #include "s_sound.h" @@ -22,6 +23,7 @@ #include "m_misc.h" #include "r_things.h" #include "i_video.h" +#include "z_zone.h" #include "lua_hook.h" #ifdef HW3SOUND @@ -266,6 +268,18 @@ void A_WhoCaresIfYourSonIsABee(mobj_t *actor); void A_ParentTriesToSleep(mobj_t *actor); void A_CryingToMomma(mobj_t *actor); void A_CheckFlags2(mobj_t *actor); +void A_Boss5FindWaypoint(mobj_t *actor); +void A_DoNPCSkid(mobj_t *actor); +void A_DoNPCPain(mobj_t *actor); +void A_PrepareRepeat(mobj_t *actor); +void A_Boss5ExtraRepeat(mobj_t *actor); +void A_Boss5Calm(mobj_t *actor); +void A_Boss5CheckOnGround(mobj_t *actor); +void A_Boss5CheckFalling(mobj_t *actor); +void A_Boss5PinchShot(mobj_t *actor); +void A_Boss5MakeItRain(mobj_t *actor); +void A_LookForBetter(mobj_t *actor); +void A_Boss5BombExplode(mobj_t *actor); //for p_enemy.c // @@ -3549,59 +3563,103 @@ bossjustdie: else if (P_MobjWasRemoved(mo)) return; #endif - if (mo->type == MT_BLACKEGGMAN || mo->type == MT_CYBRAKDEMON) + switch (mo->type) { - mo->flags |= MF_NOCLIP; - mo->flags &= ~MF_SPECIAL; + case MT_BLACKEGGMAN: + case MT_CYBRAKDEMON: + { + mo->flags |= MF_NOCLIP; + mo->flags &= ~MF_SPECIAL; - S_StartSound(NULL, sfx_befall); - } - else if (mo->type == MT_KOOPA) - { - junk.tag = 650; - EV_DoCeiling(&junk, raiseToHighest); - return; - } - else // eggmobiles - { - // Stop exploding and prepare to run. - P_SetMobjState(mo, mo->info->xdeathstate); - if (P_MobjWasRemoved(mo)) + S_StartSound(NULL, sfx_befall); + break; + } + case MT_KOOPA: + { + junk.tag = 650; + EV_DoCeiling(&junk, raiseToHighest); return; - - P_SetTarget(&mo->target, NULL); - - // Flee! Flee! Find a point to escape to! If none, just shoot upward! - // scan the thinkers to find the runaway point - for (th = thinkercap.next; th != &thinkercap; th = th->next) + } + case MT_FANG: { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type == MT_BOSSFLYPOINT) + if (mo->tracer) { - // If this one's closer then the last one, go for it. - if (!mo->target || - P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z) < - P_AproxDistance(P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y), mo->z - mo->target->z)) - P_SetTarget(&mo->target, mo2); - // Otherwise... Don't! + var1 = var2 = 0; + A_Boss5Jump(mo); + mo->momx = ((16 - 1)*mo->momx)/16; + mo->momy = ((16 - 1)*mo->momy)/16; + if (!(mo->flags2 & MF2_AMBUSH)) + { + const fixed_t time = FixedHypot(mo->tracer->x - mo->x, mo->tracer->y - mo->y)/FixedHypot(mo->momx, mo->momy); + const fixed_t speed = 64*FRACUNIT; + mobj_t *pole = P_SpawnMobj( + mo->tracer->x - P_ReturnThrustX(mo->tracer, mo->tracer->angle, speed*time), + mo->tracer->y - P_ReturnThrustY(mo->tracer, mo->tracer->angle, speed*time), + mo->tracer->floorz + 4*FRACUNIT, + MT_FSGNB); + P_SetTarget(&pole->tracer, P_SpawnMobj( + pole->x + P_ReturnThrustX(pole, mo->tracer->angle, FRACUNIT), + pole->y + P_ReturnThrustY(pole, mo->tracer->angle, FRACUNIT), + pole->z + 256*FRACUNIT, + MT_FSGNA)); + pole->angle = mo->tracer->angle; + pole->tracer->angle = pole->angle - ANGLE_90; + pole->momx = P_ReturnThrustX(pole, pole->angle, speed); + pole->momy = P_ReturnThrustY(pole, pole->angle, speed); + pole->tracer->momx = pole->momx; + pole->tracer->momy = pole->momy; + } } + else + { + P_SetObjectMomZ(mo, 10*FRACUNIT, false); + mo->flags |= MF_NOGRAVITY; + } + mo->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; + return; } - - mo->flags |= MF_NOGRAVITY|MF_NOCLIP; - mo->flags |= MF_NOCLIPHEIGHT; - - if (mo->target) + default: //eggmobiles { - mo->angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y); - mo->flags2 |= MF2_BOSSFLEE; - mo->momz = FixedMul(FixedDiv(mo->target->z - mo->z, P_AproxDistance(mo->x-mo->target->x,mo->y-mo->target->y)), FixedMul(2*FRACUNIT, mo->scale)); + // Stop exploding and prepare to run. + P_SetMobjState(mo, mo->info->xdeathstate); + if (P_MobjWasRemoved(mo)) + return; + + P_SetTarget(&mo->target, NULL); + + // Flee! Flee! Find a point to escape to! If none, just shoot upward! + // scan the thinkers to find the runaway point + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type == MT_BOSSFLYPOINT) + { + // If this one's closer then the last one, go for it. + if (!mo->target || + P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z) < + P_AproxDistance(P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y), mo->z - mo->target->z)) + P_SetTarget(&mo->target, mo2); + // Otherwise... Don't! + } + } + + mo->flags |= MF_NOGRAVITY|MF_NOCLIP; + mo->flags |= MF_NOCLIPHEIGHT; + + if (mo->target) + { + mo->angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y); + mo->flags2 |= MF2_BOSSFLEE; + mo->momz = FixedMul(FixedDiv(mo->target->z - mo->z, P_AproxDistance(mo->x-mo->target->x,mo->y-mo->target->y)), FixedMul(2*FRACUNIT, mo->scale)); + } + else + mo->momz = FixedMul(2*FRACUNIT, mo->scale); + break; } - else - mo->momz = FixedMul(2*FRACUNIT, mo->scale); } if (mo->type == MT_EGGMOBILE2) @@ -11852,3 +11910,614 @@ void A_CheckFlags2(mobj_t *actor) if (actor->flags2 & locvar1) P_SetMobjState(actor, (statenum_t)locvar2); } + +// Function: A_Boss5FindWaypoint +// +// Description: Finds the next waypoint in sequence and sets it as its tracer. +// +// var1 = if 1, always go to ambush-marked waypoint. if 2, go to MT_BOSSFLYPOINT. +// var2 = unused +// +void A_Boss5FindWaypoint(mobj_t *actor) +{ + INT32 locvar1 = var1; + //INT32 locvar2 = var2; + boolean avoidcenter; + UINT32 i; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss5FindWaypoint", actor)) + return; +#endif + + avoidcenter = !actor->tracer || (actor->health == actor->info->damage+1); + + if (locvar1 == 2) // look for the boss waypoint + { + for (i = 0; i < nummapthings; i++) + { + if (!mapthings[i].mobj) + continue; + if (mapthings[i].mobj->type != MT_BOSSFLYPOINT) + continue; + P_SetTarget(&actor->tracer, mapthings[i].mobj); + break; + } + if (i == nummapthings) + return; // no boss flypoints found + } + else if (locvar1 == 1) // always go to ambush-marked waypoint + { + if (avoidcenter) + goto nowaypoints; // if we can't go the center, why on earth are we doing this? + + for (i = 0; i < nummapthings; i++) + { + if (!mapthings[i].mobj) + continue; + if (mapthings[i].mobj->type != MT_FANGWAYPOINT) + continue; + if (mapthings[i].options & MTF_AMBUSH) + { + P_SetTarget(&actor->tracer, mapthings[i].mobj); + break; + } + } + + if (i == nummapthings) + goto nowaypoints; + } + else // locvar1 == 0 + { + fixed_t hackoffset = P_MobjFlip(actor)*56*FRACUNIT; + INT32 numwaypoints = 0; + mobj_t **waypoints; + INT32 key; + + actor->z += hackoffset; + + // first, count how many waypoints we have + for (i = 0; i < nummapthings; i++) + { + if (!mapthings[i].mobj) + continue; + if (mapthings[i].mobj->type != MT_FANGWAYPOINT) + continue; + if (actor->tracer == mapthings[i].mobj) // this was your tracer last time + continue; + if (mapthings[i].options & MTF_AMBUSH) + { + if (avoidcenter) + continue; + } + else if (mapthings[i].mobj->reactiontime > 0) + continue; + if (!P_CheckSight(actor, mapthings[i].mobj)) + continue; + numwaypoints++; + } + + // players also count as waypoints apparently + if (actor->extravalue2 > 1) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + if (!players[i].mo) + continue; + if (players[i].spectator) + continue; + if (players[i].mo->health <= 0) + continue; + if (players[i].powers[pw_flashing]) + continue; + if (actor->tracer == players[i].mo) // this was your tracer last time + continue; + if (!P_CheckSight(actor, players[i].mo)) + continue; + numwaypoints++; + } + } + + if (!numwaypoints) + { + // restore z position + actor->z -= hackoffset; + goto nowaypoints; // no waypoints :( + } + + // allocate the table and reset count to zero + waypoints = Z_Calloc(sizeof(*waypoints)*numwaypoints, PU_STATIC, NULL); + numwaypoints = 0; + + // now find them again and add them to the table! + for (i = 0; i < nummapthings; i++) + { + if (!mapthings[i].mobj) + continue; + if (mapthings[i].mobj->type != MT_FANGWAYPOINT) + continue; + if (actor->tracer == mapthings[i].mobj) // this was your tracer last time + continue; + if (mapthings[i].options & MTF_AMBUSH) + { + if (avoidcenter) + continue; + } + else if (mapthings[i].mobj->reactiontime > 0) + { + mapthings[i].mobj->reactiontime--; + continue; + } + if (!P_CheckSight(actor, mapthings[i].mobj)) + continue; + waypoints[numwaypoints++] = mapthings[i].mobj; + } + + if (actor->extravalue2 > 1) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + if (!players[i].mo) + continue; + if (players[i].spectator) + continue; + if (players[i].mo->health <= 0) + continue; + if (players[i].powers[pw_flashing]) + continue; + if (actor->tracer == players[i].mo) // this was your tracer last time + continue; + if (!P_CheckSight(actor, players[i].mo)) + continue; + waypoints[numwaypoints++] = players[i].mo; + } + } + + // restore z position + actor->z -= hackoffset; + + if (!numwaypoints) + { + Z_Free(waypoints); // free table + goto nowaypoints; // ??? + } + + key = P_RandomKey(numwaypoints); + + P_SetTarget(&actor->tracer, waypoints[key]); + if (actor->tracer->type == MT_FANGWAYPOINT) + actor->tracer->reactiontime = numwaypoints/4; // Monster Iestyn: is this how it should be? I count center waypoints as waypoints unlike the original Lua script + Z_Free(waypoints); // free table + } + + // now face the tracer you just set! + A_FaceTracer(actor); + return; + +nowaypoints: + // no waypoints at all, guess the mobj has to disappear + if (actor->health) + P_KillMobj(actor, NULL, NULL, 0); + else + P_RemoveMobj(actor); + return; +} + +// Function: A_DoNPCSkid +// +// Description: Something that looks like a player is skidding. +// +// var1 = state to change to upon being slow enough +// var2 = minimum speed +// +void A_DoNPCSkid(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + fixed_t x, y, z; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_DoNPCSkid", actor)) + return; +#endif + + x = actor->x; + y = actor->y; + z = actor->z; + + if (!locvar2) + locvar2 = FRACUNIT/2; + + if ((FixedHypot(actor->momx, actor->momy) < locvar2) + || !P_TryMove(actor, actor->x + actor->momx, actor->y + actor->momy, false)) + { + actor->momx = actor->momy = 0; + P_SetMobjState(actor, locvar1); + return; + } + else + { + actor->momx = (2*actor->momx)/3; + actor->momy = (2*actor->momy)/3; + } + + P_TeleportMove(actor, x, y, z); + + // Spawn a particle every 3 tics. + if (!(leveltime % 3)) + { + mobj_t *particle = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_SPINDUST); + particle->tics = 10; + + P_SetScale(particle, 2*actor->scale/3); + particle->destscale = actor->scale; + P_SetObjectMomZ(particle, FRACUNIT, false); + } +} + +// Function: A_DoNPCPain +// +// Description: Something that looks like a player was hit, put them in pain. +// +// var1 = If zero, always fling the same amount. +// Otherwise, slowly reduce the vertical +// and horizontal speed to the base value +// multiplied by this the more damage is done. +// var2 = If zero, use default fling values. +// Otherwise, vertical and horizontal speed +// will be multiplied by this. +// +void A_DoNPCPain(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + fixed_t vspeed = 0; + fixed_t hspeed = FixedMul(4*FRACUNIT, actor->scale); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_DoNPCPain", actor)) + return; +#endif + + actor->flags &= ~(MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT); + + var1 = var2 = 0; + A_Pain(actor); + + actor->z += P_MobjFlip(actor); + + if (actor->eflags & MFE_UNDERWATER) + vspeed = FixedDiv(10511*FRACUNIT,2600*FRACUNIT); + else + vspeed = FixedDiv(69*FRACUNIT,10*FRACUNIT); + + if (actor->target) + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + actor->target->momx, actor->target->y + actor->target->momy); + + if (locvar1) + { + if (!actor->info->spawnhealth) + return; // there's something very wrong here if you're using this action on something with no starting health + locvar1 += ((FRACUNIT - locvar1)/actor->info->spawnhealth)*actor->health; + hspeed = FixedMul(hspeed, locvar1); + vspeed = FixedMul(vspeed, locvar1); + } + + if (locvar2) + { + hspeed = FixedMul(hspeed, locvar2); + vspeed = FixedMul(vspeed, locvar2); + } + + P_SetObjectMomZ(actor, vspeed, false); + P_InstaThrust(actor, actor->angle, -hspeed); +} + +// Function: A_PrepareRepeat +// +// Description: Simple way to prepare A_Repeat. +// +// var1 = value to set extravalue2 to +// var2 = unused +// +void A_PrepareRepeat(mobj_t *actor) +{ + INT32 locvar1 = var1; + //INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_PrepareRepeat", actor)) + return; +#endif + + actor->extravalue2 = locvar1; +} + +// Function: A_Boss5ExtraRepeat +// +// Description: Simple way to prepare A_Repeat. +// +// var1 = maximum value to setextravalue2 to (normally) +// var2 = pinch annoyance +// +void A_Boss5ExtraRepeat(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + INT32 calc; + INT32 locspawn; + INT32 lochealth; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss5ExtraRepeat", actor)) + return; +#endif + + if (actor->extravalue2 > 0 && !(actor->flags2 & MF2_FRET)) + return; + + locspawn = actor->info->spawnhealth - actor->info->damage; + lochealth = actor->health - actor->info->damage; + + if (locspawn <= 0 || lochealth <= 0) + calc = locvar1; + else + calc = (locvar1*(locspawn - lochealth))/locspawn; + + if (calc > 2) + actor->extravalue2 = 1 + calc/2 + P_RandomKey(calc/2); + else + actor->extravalue2 = 1 + calc; + + if (lochealth <= 0) + actor->extravalue2 += locvar2; +} + +// Function: A_Boss5Calm +// +// Description: Simple way to disable MF2_FRET (and enable MF_SHOOTABLE the first time it's called) +// +// var1 = unused +// var2 = unused +// +void A_Boss5Calm(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss5Calm", actor)) + return; +#endif + actor->flags |= MF_SHOOTABLE; + actor->flags2 &= ~MF2_FRET; +} + +// Function: A_Boss5CheckOnGround +// +// Description: Ground checker. +// +// var1 = state to change to upon hitting ground. +// var2 = state to change to upon hitting ground if health == pinchhealth, assuming it exists +// +void A_Boss5CheckOnGround(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss5CheckOnGround", actor)) + return; +#endif + + if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) + || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height >= actor->ceilingz)) + { + if (locvar2 && (!actor->health || (actor->health == actor->info->damage && !(actor->flags2 & MF2_STRONGBOX)))) + P_SetMobjState(actor, locvar2); + else + P_SetMobjState(actor, locvar1); + } + + if (actor->tracer && P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y) < 2*actor->radius) + { + actor->momx = (4*actor->momx)/5; + actor->momy = (4*actor->momy)/5; + } +} + +// Function: A_Boss5CheckFalling +// +// Description: Falling checker. +// +// var1 = state to change to when hitting ground. +// var2 = state to change to when falling. +// +void A_Boss5CheckFalling(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss5CheckFalling", actor)) + return; +#endif + + if (actor->health && actor->extravalue2 > 1) + { + var1 = locvar1; + var2 = 0; + A_Boss5CheckOnGround(actor); + return; + } + + if (P_MobjFlip(actor)*actor->momz <= 0) + P_SetMobjState(actor, locvar2); +} + +// Function: A_Boss5PinchShot +// +// Description: Fires a missile directly upwards if in pinch. +// +// var1 = object # to shoot +// var2 = height offset (from default of +48 FU) +// +void A_Boss5PinchShot(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + fixed_t zoffset; + mobj_t *missile; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss5PinchShot", actor)) + return; +#endif + + if (actor->health > actor->info->damage) + return; + + if (actor->eflags & MFE_VERTICALFLIP) + zoffset = actor->z + actor->height - FixedMul((48 + locvar2)*FRACUNIT, actor->scale); + else + zoffset = actor->z + FixedMul((48 + locvar2)*FRACUNIT, actor->scale); + + missile = P_SpawnPointMissile(actor, actor->x, actor->y, zoffset, locvar1, + actor->x, actor->y, zoffset); + + if (!missile) + return; + + missile->momx = missile->momy = 0; + missile->momz = P_MobjFlip(actor)*missile->info->speed/2; +} + +// Function: A_Boss5MakeItRain +// +// Description: Pinch crisis. +// +// var1 = object # to shoot +// var2 = height offset (from default of +48 FU) +// +void A_Boss5MakeItRain(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + INT32 offset = (48 + locvar2)<<16; // upper 16 bits, not fixed_t! + INT32 i; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss5MakeItRain", actor)) + return; +#endif + + actor->flags2 |= MF2_STRONGBOX; + + var1 = locvar1; + var2 = offset + 90; + A_TrapShot(actor); + + for (i = 0; i < 8; i++) + { + actor->angle += ANGLE_45; + + var1 = locvar1; + var2 = offset + (i & 1) ? 55 : 70; + A_TrapShot(actor); + } + + actor->extravalue2 = 0; +} + +// Function: A_LookForBetter +// +// Description: A_Look, except it finds a better target in multiplayer, and doesn't lose the target in singleplayer. +// +// var1 lower 16 bits = 0 - looks only in front, 1 - looks all around +// var1 upper 16 bits = distance limit +// var2 = unused +// +void A_LookForBetter(mobj_t *actor) +{ + INT32 locvar1 = var1; + //INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_LookForBetter", actor)) + return; +#endif + + P_LookForPlayers(actor, (locvar1 & 65535), false, FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale)); + A_FaceTarget(actor); +} + +/* * Spawns a dust ring. + * The dust ring behaves slightly randomly so it doesn't look too uniform. + * + * \param mobjtype Thing type to make a ring of. + * \param div Amount of things to spawn on the ring. + * \param x Center X coordinates. + * \param y Center Y coordinates. + * \param z Center Z coordinates. + * \param radius Radius. + * \param speed Additional thrust on particles. + * \param scale Scale. + */ +static void P_DustRing(mobjtype_t mobjtype, UINT32 div, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t speed, fixed_t scale) +{ + angle_t ang = FixedAngle(FixedDiv(360*FRACUNIT, div*FRACUNIT)); //(ANGLE_180/div)*2; + UINT32 i; + + // it turned out the radius was effectively nullified thanks to errors in the original script + // BUT people preferred how it looked before I "fixed" it, so I got rid of the radius calculations altogether + // this was a bit of a mess to sort out, but at least it's probably somewhat fine now? + // -- Monster Iestyn (21/05/19) + (void)radius; + + for (i = 0; i < div; i++) + { + mobj_t *dust = P_SpawnMobj( + x, //+ FixedMul(radius, FINECOSINE((ang*i) >> ANGLETOFINESHIFT)), + y, //+ FixedMul(radius, FINESINE((ang*i) >> ANGLETOFINESHIFT)), + z, + mobjtype + ); + + dust->angle = ang*i + ANGLE_90; + P_SetScale(dust, scale); + dust->destscale = FixedMul(4*FRACUNIT + P_RandomFixed(), scale); + dust->scalespeed = scale/24; + P_Thrust(dust, ang*i, speed + FixedMul(P_RandomFixed(), scale)); + dust->momz = P_SignedRandom()*scale/64; + } +} + +// Function: A_Boss5BombExplode +// +// Description: Boss 5's bomb exploding. +// +// var1 = Thing type to spawn as dust +// var2 = unused +// +void A_Boss5BombExplode(mobj_t *actor) +{ + INT32 locvar1 = var1; + //INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss5BombExplode", actor)) + return; +#endif + + // The original Lua script did not use |= to add flags but just set these flags exactly apparently? + // (I may modify this later) + // -- Monster Iestyn (21/05/19) + actor->flags = MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP; + actor->flags2 = MF2_EXPLOSION; + + if (actor->target) + P_RadiusAttack(actor, actor->target, 7*actor->radius, 0); + + P_DustRing(locvar1, 4, actor->x, actor->y, actor->z+actor->height, 2*actor->radius, 0, actor->scale); + P_DustRing(locvar1, 6, actor->x, actor->y, actor->z+actor->height/2, 3*actor->radius, FRACUNIT, actor->scale); + //P_StartQuake(9*actor->scale, TICRATE/6, {actor->x, actor->y, actor->z}, 20*actor->radius); + // the above does not exist, so we set the quake values directly instead + quake.intensity = 9*actor->scale; + quake.time = TICRATE/6; + // the following quake values have no effect atm? ah well, may as well set them anyway + { + mappoint_t q_epicenter = {actor->x, actor->y, actor->z}; + quake.epicenter = &q_epicenter; + } + quake.radius = 20*actor->radius; +} diff --git a/src/p_inter.c b/src/p_inter.c index 177b8d16e..68f1782c5 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -375,44 +375,82 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) /////ENEMIES & BOSSES!!///////////////////////////////// //////////////////////////////////////////////////////// - if (special->type == MT_BLACKEGGMAN) + switch (special->type) { - P_DamageMobj(toucher, special, special, 1, 0); // ouch - return; - } - - if (special->type == MT_BIGMINE) - { - special->momx = toucher->momx/3; - special->momy = toucher->momy/3; - special->momz = toucher->momz/3; - toucher->momx /= -8; - toucher->momy /= -8; - toucher->momz /= -8; - special->flags &= ~MF_SPECIAL; - if (special->info->activesound) - S_StartSound(special, special->info->activesound); - P_SetTarget(&special->tracer, toucher); - player->homing = 0; - return; - } - - if (special->type == MT_GSNAPPER && !elementalpierce - && toucher->z < special->z + special->height && toucher->z + toucher->height > special->z - && P_DamageMobj(toucher, special, special, 1, DMG_SPIKE)) - return; // Can only hit snapper from above - - if (special->type == MT_SPINCUSHION - && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0)) - { - if (player->pflags & PF_BOUNCING) + case MT_BLACKEGGMAN: { - toucher->momz = -toucher->momz; - P_DoAbilityBounce(player, false); + P_DamageMobj(toucher, special, special, 1, 0); // ouch return; } - else if (P_DamageMobj(toucher, special, special, 1, DMG_SPIKE)) - return; // Cannot hit sharp from above + case MT_BIGMINE: + { + special->momx = toucher->momx/3; + special->momy = toucher->momy/3; + special->momz = toucher->momz/3; + toucher->momx /= -8; + toucher->momy /= -8; + toucher->momz /= -8; + special->flags &= ~MF_SPECIAL; + if (special->info->activesound) + S_StartSound(special, special->info->activesound); + P_SetTarget(&special->tracer, toucher); + player->homing = 0; + return; + } + case MT_GSNAPPER: + if (!elementalpierce + && toucher->z < special->z + special->height + && toucher->z + toucher->height > special->z + && P_DamageMobj(toucher, special, special, 1, DMG_SPIKE)) + return; // Can only hit snapper from above + break; + + case MT_SPINCUSHION: + if (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) + { + if (player->pflags & PF_BOUNCING) + { + toucher->momz = -toucher->momz; + P_DoAbilityBounce(player, false); + return; + } + else if (P_DamageMobj(toucher, special, special, 1, DMG_SPIKE)) + return; // Cannot hit sharp from above + } + break; + case MT_FANG: + if (!player->powers[pw_flashing] + && !(player->charability == CA_TWINSPIN && player->panim == PA_ABILITY) + && !(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)) + { + if ((special->state == &states[S_FANG_BOUNCE3] + || special->state == &states[S_FANG_BOUNCE4] + || special->state == &states[S_FANG_PINCHBOUNCE3] + || special->state == &states[S_FANG_PINCHBOUNCE4]) + && P_MobjFlip(special)*((special->z + special->height/2) - (toucher->z - toucher->height/2)) > 0) + { + P_DamageMobj(toucher, special, special, 1, 0); + P_SetTarget(&special->tracer, toucher); + + if (special->state == &states[S_FANG_PINCHBOUNCE3] + || special->state == &states[S_FANG_PINCHBOUNCE4]) + P_SetMobjState(special, S_FANG_PINCHPATHINGSTART2); + else + { + var1 = var2 = 4; + A_Boss5ExtraRepeat(special); + P_SetMobjState(special, S_FANG_PATHINGCONT2); //S_FANG_PATHINGCONT1 if you want him to drop a bomb on the player + } + if (special->eflags & MFE_VERTICALFLIP) + special->z = toucher->z - special->height; + else + special->z = toucher->z + toucher->height; + return; + } + } + break; + default: + break; } if (((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING)) @@ -3352,9 +3390,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da else if (player->powers[pw_invulnerability] || player->powers[pw_flashing] // ignore bouncing & such in invulnerability || player->powers[pw_super]) { - if (force || (inflictor && (inflictor->flags & MF_MISSILE) - && (inflictor->flags2 & MF2_SUPERFIRE) - && player->powers[pw_super])) + if (force + || (player->powers[pw_super] + && inflictor && inflictor->flags & MF_MISSILE && inflictor->flags2 & MF2_SUPERFIRE) // Super Sonic is stunned! + || (player->powers[pw_flashing] + && source && source->type == MT_FANG && inflictor && inflictor->type == MT_CORK)) // Fang's cork bullets knock you back even when flashing { #ifdef HAVE_BLUA if (!LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) @@ -3362,8 +3402,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da P_SuperDamage(player, inflictor, source, damage); return true; } - else - return false; + return false; } #ifdef HAVE_BLUA else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) diff --git a/src/p_map.c b/src/p_map.c index ceaa6ca24..ffe6cf916 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -551,6 +551,44 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails) } } +// Boss 5 post-defeat comedy +static void P_SlapStick(mobj_t *fang, mobj_t *pole) +{ + fixed_t momx1, momx2, momy1, momy2; + +#define dist 3 + momx1 = pole->momx/dist; + momy1 = pole->momy/dist; + momx2 = fang->momx/dist; + momy2 = fang->momy/dist; + + pole->tracer->momx = momx1 + (dist-1)*momx2; + pole->tracer->momy = momy1 + (dist-1)*momy2; + fang->momx = (dist-1)*momx1 + momx2; + fang->momy = (dist-1)*momy1 + momy2; +#undef dist + + P_SetMobjState(pole, pole->info->deathstate); + + P_SetObjectMomZ(pole->tracer, 6*FRACUNIT, false); + pole->tracer->flags &= ~(MF_NOGRAVITY|MF_NOCLIP); + pole->tracer->movedir = ANGLE_67h; + if ((R_PointToAngle(fang->x - pole->tracer->x, fang->y - pole->tracer->y) - pole->angle) > ANGLE_180) + pole->tracer->movedir = InvAngle(pole->tracer->movedir); + + P_SetObjectMomZ(fang, 14*FRACUNIT, false); + fang->flags |= MF_NOGRAVITY|MF_NOCLIP; + P_SetMobjState(fang, fang->info->xdeathstate); + + pole->tracer->tics = pole->tics = fang->tics; + + var1 = var2 = 0; + A_Scream(pole->tracer); + S_StartSound(fang, sfx_altdi1); + + P_SetTarget(&pole->tracer, NULL); +} + // // PIT_CheckThing // @@ -780,6 +818,20 @@ static boolean PIT_CheckThing(mobj_t *thing) } #endif + if (tmthing->type == MT_FANG && thing->type == MT_FSGNB) + { + if (thing->z > tmthing->z + tmthing->height) + return true; // overhead + if (thing->z + thing->height < tmthing->z) + return true; // underneath + if (!thing->tracer) + return true; + P_SlapStick(tmthing, thing); + // no return value was used in the original prototype script at this point, + // so I'm assuming we fall back on the solid code to determine how it all ends? + // -- Monster Iestyn + } + // Billiards mines! if (thing->type == MT_BIGMINE) { diff --git a/src/p_mobj.c b/src/p_mobj.c index 5457c4f68..bc43fbe1e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5013,6 +5013,47 @@ static void P_Boss4Thinker(mobj_t *mobj) A_FaceTarget(mobj); } +// +// AI for the fifth boss. +// +static void P_Boss5Thinker(mobj_t *mobj) +{ + if (!mobj->health) + { + if (mobj->state == &states[mobj->info->xdeathstate]) + mobj->momz -= (2*FRACUNIT)/3; + else if (mobj->tracer && P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y) < 2*mobj->radius) + mobj->flags &= ~MF_NOCLIP; + } + else + { + if (mobj->flags2 & MF2_FRET && (leveltime & 1) + && mobj->state != &states[S_FANG_PAIN1] && mobj->state != &states[S_FANG_PAIN2]) + mobj->flags2 |= MF2_DONTDRAW; + else + mobj->flags2 &= ~MF2_DONTDRAW; + } + + if (mobj->state == &states[S_FANG_BOUNCE3] + || mobj->state == &states[S_FANG_BOUNCE4] + || mobj->state == &states[S_FANG_PINCHBOUNCE3] + || mobj->state == &states[S_FANG_PINCHBOUNCE4]) + { + if (P_MobjFlip(mobj)*mobj->momz > 0 + && abs(mobj->momx) < FRACUNIT/2 && abs(mobj->momy) < FRACUNIT/2 + && !P_IsObjectOnGround(mobj)) + { + mobj_t *prevtarget = mobj->target; + P_SetTarget(&mobj->target, NULL); + var1 = var2 = 0; + A_DoNPCPain(mobj); + P_SetTarget(&mobj->target, prevtarget); + P_SetMobjState(mobj, S_FANG_WALLHIT); + mobj->extravalue2++; + } + } +} + // // AI for Black Eggman // Note: You CANNOT have more than ONE Black Eggman @@ -7289,6 +7330,10 @@ void P_MobjThinker(mobj_t *mobj) mobj->angle += (angle_t)mobj->movecount; } break; + case MT_FSGNA: + if (mobj->movedir) + mobj->angle += mobj->movedir; + break; default: if (mobj->fuse) { // Scenery object fuse! Very basic! @@ -7378,6 +7423,9 @@ void P_MobjThinker(mobj_t *mobj) case MT_EGGMOBILE4: P_Boss4Thinker(mobj); break; + case MT_FANG: + P_Boss5Thinker(mobj); + break; case MT_BLACKEGGMAN: P_Boss7Thinker(mobj); break; @@ -9100,6 +9148,10 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_NIGHTSSTAR: if (nummaprings >= 0) nummaprings++; + break; + case MT_FBOMB: + mobj->flags2 |= MF2_EXPLOSION; + break; default: break; } diff --git a/src/p_sight.c b/src/p_sight.c index 556041585..aa59314f1 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -14,6 +14,7 @@ #include "doomdef.h" #include "doomstat.h" #include "p_local.h" +#include "p_slopes.h" #include "r_main.h" #include "r_state.h" @@ -216,6 +217,10 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) const sector_t *front, *back; const vertex_t *v1,*v2; fixed_t frac; + fixed_t frontf, backf, frontc, backc; +#ifdef ESLOPE + fixed_t fracx, fracy; +#endif // already checked other side? if (line->validcount == validcount) @@ -250,36 +255,51 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) if (!(line->flags & ML_TWOSIDED)) return false; + // calculate fractional intercept (how far along we are divided by how far we are from t2) + frac = P_InterceptVector2(&los->strace, &divl); + + front = seg->frontsector; + back = seg->backsector; +#ifdef ESLOPE + // calculate position at intercept + fracx = los->strace.x + FixedMul(los->strace.dx, frac); + fracy = los->strace.y + FixedMul(los->strace.dy, frac); + // calculate sector heights + frontf = (front->f_slope) ? P_GetZAt(front->f_slope, fracx, fracy) : front->floorheight; + frontc = (front->c_slope) ? P_GetZAt(front->c_slope, fracx, fracy) : front->ceilingheight; + backf = (back->f_slope) ? P_GetZAt(back->f_slope, fracx, fracy) : back->floorheight; + backc = (back->c_slope) ? P_GetZAt(back->c_slope, fracx, fracy) : back->ceilingheight; +#else + frontf = front->floorheight; + frontc = front->ceilingheight; + backf = back->floorheight; + backc = back->ceilingheight; +#endif // crosses a two sided line // no wall to block sight with? - if ((front = seg->frontsector)->floorheight == - (back = seg->backsector)->floorheight && - front->ceilingheight == back->ceilingheight) + if (frontf == backf && frontc == backc + && !front->ffloors & !back->ffloors) // (and no FOFs) continue; // possible occluder // because of ceiling height differences - popentop = front->ceilingheight < back->ceilingheight ? - front->ceilingheight : back->ceilingheight ; + popentop = min(frontc, backc); // because of floor height differences - popenbottom = front->floorheight > back->floorheight ? - front->floorheight : back->floorheight ; + popenbottom = max(frontf, backf); // quick test for totally closed doors if (popenbottom >= popentop) return false; - frac = P_InterceptVector2(&los->strace, &divl); - - if (front->floorheight != back->floorheight) + if (frontf != backf) { fixed_t slope = FixedDiv(popenbottom - los->sightzstart , frac); if (slope > los->bottomslope) los->bottomslope = slope; } - if (front->ceilingheight != back->ceilingheight) + if (frontc != backc) { fixed_t slope = FixedDiv(popentop - los->sightzstart , frac); if (slope < los->topslope) @@ -288,6 +308,58 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) if (los->topslope <= los->bottomslope) return false; + + // Monster Iestyn: check FOFs! + if (front->ffloors || back->ffloors) + { + ffloor_t *rover; + fixed_t topslope, bottomslope; + fixed_t topz, bottomz; + // check front sector's FOFs first + for (rover = front->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) + || !(rover->flags & FF_RENDERSIDES) || rover->flags & FF_TRANSLUCENT) + { + continue; + } + +#ifdef ESLOPE + topz = (*rover->t_slope) ? P_GetZAt(*rover->t_slope, fracx, fracy) : *rover->topheight; + bottomz = (*rover->b_slope) ? P_GetZAt(*rover->b_slope, fracx, fracy) : *rover->bottomheight; +#else + topz = *rover->topheight; + bottomz = *rover->bottomheight; +#endif + topslope = FixedDiv(topz - los->sightzstart , frac); + bottomslope = FixedDiv(bottomz - los->sightzstart , frac); + if (topslope >= los->topslope && bottomslope <= los->bottomslope) + return false; // view completely blocked + } + // check back sector's FOFs as well + for (rover = back->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) + || !(rover->flags & FF_RENDERSIDES) || rover->flags & FF_TRANSLUCENT) + { + continue; + } + +#ifdef ESLOPE + topz = (*rover->t_slope) ? P_GetZAt(*rover->t_slope, fracx, fracy) : *rover->topheight; + bottomz = (*rover->b_slope) ? P_GetZAt(*rover->b_slope, fracx, fracy) : *rover->bottomheight; +#else + topz = *rover->topheight; + bottomz = *rover->bottomheight; +#endif + topslope = FixedDiv(topz - los->sightzstart , frac); + bottomslope = FixedDiv(bottomz - los->sightzstart , frac); + if (topslope >= los->topslope && bottomslope <= los->bottomslope) + return false; // view completely blocked + } + // TODO: figure out if it's worth considering partially blocked cases or not? + // maybe to adjust los's top/bottom slopes if needed + } } // passed the subsector ok @@ -398,6 +470,8 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) if (s1 == s2) // Both sectors are the same. { ffloor_t *rover; + fixed_t topz1, bottomz1; // top, bottom heights at t1's position + fixed_t topz2, bottomz2; // likewise but for t2 for (rover = s1->ffloors; rover; rover = rover->next) { @@ -410,9 +484,30 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) continue; } +#ifdef ESLOPE + if (*rover->t_slope) + { + topz1 = P_GetZAt(*rover->t_slope, t1->x, t1->y); + topz2 = P_GetZAt(*rover->t_slope, t2->x, t2->y); + } + else + topz1 = topz2 = *rover->topheight; + + if (*rover->b_slope) + { + bottomz1 = P_GetZAt(*rover->b_slope, t1->x, t1->y); + bottomz2 = P_GetZAt(*rover->b_slope, t2->x, t2->y); + } + else + bottomz1 = bottomz2 = *rover->bottomheight; +#else + topz1 = topz2 = *rover->topheight; + bottomz1 = bottomz2 = *rover->bottomheight; +#endif + // Check for blocking floors here. - if ((los.sightzstart < *rover->bottomheight && t2->z >= *rover->topheight) - || (los.sightzstart >= *rover->topheight && t2->z + t2->height < *rover->bottomheight)) + if ((los.sightzstart < bottomz1 && t2->z >= topz2) + || (los.sightzstart >= topz1 && t2->z + t2->height < bottomz2)) { // no way to see through that return false; @@ -423,19 +518,19 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) if (!(rover->flags & FF_INVERTPLANES)) { - if (los.sightzstart >= *rover->topheight && t2->z + t2->height < *rover->topheight) + if (los.sightzstart >= topz1 && t2->z + t2->height < topz2) return false; // blocked by upper outside plane - if (los.sightzstart < *rover->bottomheight && t2->z >= *rover->bottomheight) + if (los.sightzstart < bottomz1 && t2->z >= bottomz2) return false; // blocked by lower outside plane } if (rover->flags & FF_INVERTPLANES || rover->flags & FF_BOTHPLANES) { - if (los.sightzstart < *rover->topheight && t2->z >= *rover->topheight) + if (los.sightzstart < topz1 && t2->z >= topz2) return false; // blocked by upper inside plane - if (los.sightzstart >= *rover->bottomheight && t2->z + t2->height < *rover->bottomheight) + if (los.sightzstart >= bottomz1 && t2->z + t2->height < bottomz2) return false; // blocked by lower inside plane } } diff --git a/src/p_spec.c b/src/p_spec.c index f8aefb9c8..e47b5cc03 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -7746,22 +7746,11 @@ static void P_SpawnScrollers(void) for (i = 0; i < numlines; i++, l++) { - fixed_t dx = l->dx; // direction and speed of scrolling - fixed_t dy = l->dy; + fixed_t dx = l->dx >> SCROLL_SHIFT; // direction and speed of scrolling + fixed_t dy = l->dy >> SCROLL_SHIFT; INT32 control = -1, accel = 0; // no control sector or acceleration INT32 special = l->special; - // If front texture X offset provided, override the amount with it. - if (sides[l->sidenum[0]].textureoffset != 0) - { - fixed_t len = sides[l->sidenum[0]].textureoffset; - fixed_t h = FixedHypot(dx, dy); - dx = FixedMul(FixedDiv(dx, h), len); - dy = FixedMul(FixedDiv(dy, h), len); - } - dx = dx >> SCROLL_SHIFT; - dy = dy >> SCROLL_SHIFT; - // These types are same as the ones they get set to except that the // first side's sector's heights cause scrolling when they change, and // this linedef controls the direction and speed of the scrolling. The @@ -7796,14 +7785,8 @@ static void P_SpawnScrollers(void) case 513: // scroll effect ceiling case 533: // scroll and carry objects on ceiling - if (l->tag == 0) - Add_Scroller(sc_ceiling, -dx, dy, control, l->frontsector - sectors, accel, l->flags & ML_NOCLIMB); - else - { - for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) - Add_Scroller(sc_ceiling, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB); - } - + for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) + Add_Scroller(sc_ceiling, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB); if (special != 533) break; /* FALLTHRU */ @@ -7811,26 +7794,14 @@ static void P_SpawnScrollers(void) case 523: // carry objects on ceiling dx = FixedMul(dx, CARRYFACTOR); dy = FixedMul(dy, CARRYFACTOR); - - if (l->tag == 0) - Add_Scroller(sc_carry_ceiling, dx, dy, control, l->frontsector - sectors, accel, l->flags & ML_NOCLIMB); - else - { - for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) - Add_Scroller(sc_carry_ceiling, dx, dy, control, s, accel, l->flags & ML_NOCLIMB); - } + for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) + Add_Scroller(sc_carry_ceiling, dx, dy, control, s, accel, l->flags & ML_NOCLIMB); break; case 510: // scroll effect floor case 530: // scroll and carry objects on floor - if (l->tag == 0) - Add_Scroller(sc_floor, -dx, dy, control, l->frontsector - sectors, accel, l->flags & ML_NOCLIMB); - else - { - for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) - Add_Scroller(sc_floor, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB); - } - + for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) + Add_Scroller(sc_floor, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB); if (special != 530) break; /* FALLTHRU */ @@ -7838,14 +7809,8 @@ static void P_SpawnScrollers(void) case 520: // carry objects on floor dx = FixedMul(dx, CARRYFACTOR); dy = FixedMul(dy, CARRYFACTOR); - - if (l->tag == 0) - Add_Scroller(sc_carry, dx, dy, control, l->frontsector - sectors, accel, l->flags & ML_NOCLIMB); - else - { - for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) - Add_Scroller(sc_carry, dx, dy, control, s, accel, l->flags & ML_NOCLIMB); - } + for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) + Add_Scroller(sc_carry, dx, dy, control, s, accel, l->flags & ML_NOCLIMB); break; // scroll wall according to linedef @@ -9208,77 +9173,43 @@ static void P_SpawnPushers(void) line_t *l = lines; register INT32 s; mobj_t *thing; - pushertype_e pushertype; - fixed_t dx, dy; for (i = 0; i < numlines; i++, l++) - { switch (l->special) { - case 541: // wind - pushertype = p_wind; - break; - - case 544: // current - pushertype = p_current; - break; - case 547: // push/pull - if (l->tag == 0) - { - s = l->frontsector - sectors; - if ((thing = P_GetPushThing(s)) != NULL) // No MT_P* means no effect - Add_Pusher(p_push, l->dx, l->dy, thing, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); - } - else + case 541: // wind + for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) + Add_Pusher(p_wind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); + break; + case 544: // current + for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) + Add_Pusher(p_current, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); + break; + case 547: // push/pull for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) { - if ((thing = P_GetPushThing(s)) != NULL) // No MT_P* means no effect + thing = P_GetPushThing(s); + if (thing) // No MT_P* means no effect Add_Pusher(p_push, l->dx, l->dy, thing, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); } - - continue; - - case 545: // current up - pushertype = p_upcurrent; - break; - - case 546: // current down - pushertype = p_downcurrent; - break; - - case 542: // wind up - pushertype = p_upwind; - break; - - case 543: // wind down - pushertype = p_downwind; - break; - - default: - continue; + break; + case 545: // current up + for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) + Add_Pusher(p_upcurrent, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); + break; + case 546: // current down + for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) + Add_Pusher(p_downcurrent, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); + break; + case 542: // wind up + for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) + Add_Pusher(p_upwind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); + break; + case 543: // wind down + for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) + Add_Pusher(p_downwind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); + break; } - - dx = l->dx; - dy = l->dy; - - // Obtain versor and scale it up according to texture offset, if provided; line length is ignored in this case. - - if (sides[l->sidenum[0]].textureoffset != 0) - { - fixed_t len = sides[l->sidenum[0]].textureoffset; - fixed_t h = FixedHypot(dx, dy); - dx = FixedMul(FixedDiv(dx, h), len); - dy = FixedMul(FixedDiv(dy, h), len); - } - - if (l->tag == 0) - Add_Pusher(pushertype, dx, dy, NULL, l->frontsector - sectors, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); - else - { - for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) - Add_Pusher(pushertype, dx, dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); - } - } } static void P_SearchForDisableLinedefs(void)