// Function: A_BossDeath
//
// Description: Possibly trigger special effects when boss dies.
//
// var1 = unused
// var2 = unused
//
void A_BossDeath(mobj_t *mo)
{
thinker_t *th;
mobj_t *mo2;
line_t junk;
INT32 i;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_BossDeath", mo))
return;
#endif
if (mo->spawnpoint && mo->spawnpoint->extrainfo)
P_LinedefExecute(LE_BOSSDEAD+(mo->spawnpoint->extrainfo*LE_PARAMWIDTH), mo, NULL);
else
P_LinedefExecute(LE_BOSSDEAD, mo, NULL);
mo->health = 0;
// Boss is dead (but not necessarily fleeing...)
// Lua may use this to ignore bosses after they start fleeing
mo->flags2 |= MF2_BOSSDEAD;
// make sure there is a player alive for victory
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && ((players[i].mo && players[i].mo->health)
|| ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
break;
if (i == MAXPLAYERS)
return; // no one left alive, so do not end game
// scan the remaining thinkers to see
// if all bosses are dead
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2 != mo && (mo2->flags & MF_BOSS) && mo2->health > 0)
goto bossjustdie; // other boss not dead - just go straight to dying!
}
// victory!
P_LinedefExecute(LE_ALLBOSSESDEAD, mo, NULL);
if (stoppedclock && modeattacking) // if you're just time attacking, skip making the capsule appear since you don't need to step on it anyways.
goto bossjustdie;
if (mo->flags2 & MF2_BOSSNOTRAP)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
P_DoPlayerExit(&players[i]);
}
}
else
{
// Bring the egg trap up to the surface
junk.tag = LE_CAPSULE0;
EV_DoElevator(&junk, elevateHighest, false);
junk.tag = LE_CAPSULE1;
EV_DoElevator(&junk, elevateUp, false);
junk.tag = LE_CAPSULE2;
EV_DoElevator(&junk, elevateHighest, false);
if (mapheaderinfo[gamemap-1]->muspostbossname[0] &&
S_MusicExists(mapheaderinfo[gamemap-1]->muspostbossname, !midi_disabled, !digital_disabled))
{
// Touching the egg trap button calls P_DoPlayerExit, which calls P_RestoreMusic.
// So just park ourselves in the mapmus variables.
// But don't change the mapmus variables if they were modified from their level header values (e.g., TUNES).
boolean changed = strnicmp(mapheaderinfo[gamemap-1]->musname, S_MusicName(), 7);
if (!strnicmp(mapheaderinfo[gamemap-1]->musname, mapmusname, 7))
{
strncpy(mapmusname, mapheaderinfo[gamemap-1]->muspostbossname, 7);
mapmusname[6] = 0;
mapmusflags = (mapheaderinfo[gamemap-1]->muspostbosstrack & MUSIC_TRACKMASK) | MUSIC_RELOADRESET;
mapmusposition = mapheaderinfo[gamemap-1]->muspostbosspos;
}
// don't change if we're in another tune
// but in case we're in jingle, use our parked mapmus variables so the correct track restores
if (!changed)
S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, (1*MUSICRATE)+(MUSICRATE/2),
mapheaderinfo[gamemap-1]->muspostbossfadein);
}
}
bossjustdie:
#ifdef HAVE_BLUA
if (LUAh_BossDeath(mo))
return;
else if (P_MobjWasRemoved(mo))
return;
#endif
// Spawn your junk
switch (mo->type)
{
default:
break;
case MT_EGGMOBILE: // twin laser pods
{
mo2 = P_SpawnMobjFromMobj(mo,
P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<<FRACBITS),
P_ReturnThrustY(mo, mo->angle - ANGLE_90, 32<<FRACBITS),
32<<FRACBITS, MT_BOSSJUNK);
mo2->angle = mo->angle;
P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale);
P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
P_SetMobjState(mo2, S_BOSSEGLZ1);
mo2 = P_SpawnMobjFromMobj(mo,
P_ReturnThrustX(mo, mo->angle + ANGLE_90, 32<<FRACBITS),
P_ReturnThrustY(mo, mo->angle + ANGLE_90, 32<<FRACBITS),
32<<FRACBITS, MT_BOSSJUNK);
mo2->angle = mo->angle;
P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale);
P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
P_SetMobjState(mo2, S_BOSSEGLZ2);
}
break;
case MT_EGGMOBILE2: // twin tanks + spigot
{
mo2 = P_SpawnMobjFromMobj(mo,
P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<<FRACBITS),
P_ReturnThrustY(mo, mo->angle - ANGLE_90, 32<<FRACBITS),
32<<FRACBITS, MT_BOSSJUNK);
mo2->angle = mo->angle;
P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale);
P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
P_SetMobjState(mo2, S_BOSSTANK1);
mo2 = P_SpawnMobjFromMobj(mo,
P_ReturnThrustX(mo, mo->angle + ANGLE_90, 32<<FRACBITS),
P_ReturnThrustY(mo, mo->angle + ANGLE_90, 32<<FRACBITS),
32<<FRACBITS, MT_BOSSJUNK);
mo2->angle = mo->angle;
P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale);
P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
P_SetMobjState(mo2, S_BOSSTANK2);
mo2 = P_SpawnMobjFromMobj(mo, 0, 0,
mobjinfo[MT_EGGMOBILE2].height + (32<<FRACBITS),
MT_BOSSJUNK);
mo2->angle = mo->angle;
P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
mo2->momz += mo->momz;
P_SetMobjState(mo2, S_BOSSSPIGOT);
}
break;
case MT_EGGMOBILE3:
{
mo2 = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_BOSSJUNK);
mo2->angle = mo->angle;
P_SetMobjState(mo2, S_BOSSSEBH1);
}
break;
}
// now do another switch case for escaping
switch (mo->type)
{
case MT_BLACKEGGMAN:
{
mo->flags |= MF_NOCLIP;
mo->flags &= ~MF_SPECIAL;
S_StartSound(NULL, sfx_befall);
break;
}
case MT_CYBRAKDEMON:
{
mo->flags |= MF_NOCLIP;
mo->flags &= ~(MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT);
S_StartSound(NULL, sfx_bedie2);
P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_CYBRAKDEMON_VILE_EXPLOSION);
mo->z += P_MobjFlip(mo);
P_SetObjectMomZ(mo, 12*FRACUNIT, false);
S_StartSound(mo, sfx_bgxpld);
if (mo->spawnpoint && !(mo->spawnpoint->options & MTF_EXTRA))
P_InstaThrust(mo, R_PointToAngle2(0, 0, mo->x, mo->y), 14*FRACUNIT);
break;
}
case MT_KOOPA:
{
junk.tag = LE_KOOPA;
EV_DoCeiling(&junk, raiseToHighest);
return;
}
case MT_FANG:
{
if (mo->flags2 & MF2_SLIDEPUSH)
{
P_RemoveMobj(mo);
return;
}
if (mo->tracer)
{
var1 = var2 = 0;
A_Boss5Jump(mo);
mo->momx = ((16 - 1)*mo->momx)/16;
mo->momy = ((16 - 1)*mo->momy)/16;
{
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 + (256+1)*FRACUNIT,
MT_FSGNB);
P_SetTarget(&pole->tracer, P_SpawnMobj(
pole->x, pole->y,
pole->z - 256*FRACUNIT,
MT_FSGNB));
P_SetTarget(&pole->tracer->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->tracer->flags |= MF_NOCLIPTHING;
P_SetScale(pole, (pole->destscale = 2*FRACUNIT));
P_SetScale(pole->tracer, (pole->tracer->destscale = 2*FRACUNIT));
pole->angle = pole->tracer->angle = mo->tracer->angle;
pole->tracer->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;
pole->tracer->tracer->momx = pole->momx;
pole->tracer->tracer->momy = pole->momy;
}
}
else
{
P_SetObjectMomZ(mo, 10*FRACUNIT, false);
mo->flags |= MF_NOGRAVITY;
}
mo->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
return;
}
default: //eggmobiles
{
UINT8 extrainfo = (mo->spawnpoint ? mo->spawnpoint->extrainfo : 0);
// 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 = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type != MT_BOSSFLYPOINT)
continue;
if (mo2->spawnpoint && mo2->spawnpoint->extrainfo != extrainfo)
continue;
// If this one's further then the last one, don't 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))
continue;
// Otherwise... Do!
P_SetTarget(&mo->target, mo2);
}
mo->flags |= MF_NOGRAVITY|MF_NOCLIP;
mo->flags |= MF_NOCLIPHEIGHT;
mo->movedir = 0;
mo->extravalue1 = 35;
mo->flags2 |= MF2_BOSSFLEE;
mo->momz = P_MobjFlip(mo)*2*mo->scale;
if (mo->target)
{
angle_t diff = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y) - mo->angle;
if (diff)
{
if (diff > ANGLE_180)
diff = InvAngle(InvAngle(diff)/mo->extravalue1);
else
diff /= mo->extravalue1;
mo->movedir = diff;
}
}
break;
}
}
}