diff --git a/src/dehacked.c b/src/dehacked.c index 3cce03c6b..d6c83e108 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -5133,7 +5133,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_METALSONIC_BADBOUNCE", "S_METALSONIC_SHOOT", "S_METALSONIC_PAIN", - "S_METALSONIC_DEATH", + "S_METALSONIC_DEATH1", + "S_METALSONIC_DEATH2", + "S_METALSONIC_DEATH3", + "S_METALSONIC_DEATH4", "S_METALSONIC_FLEE1", "S_METALSONIC_FLEE2", "S_METALSONIC_FLEE3", diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index a76c9e1c8..d649eeb8e 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -5662,7 +5662,7 @@ static void HWR_ProjectSprite(mobj_t *thing) //Hurdler: 25/04/2000: now support colormap in hardware mode if ((vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && !(vis->mobj->flags & MF_GRENADEBOUNCE) && (leveltime & 1)) // Bosses "flash" { - if (vis->mobj->type == MT_CYBRAKDEMON) + if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized) vis->colormap = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE); else if (vis->mobj->type == MT_METALSONIC_BATTLE) vis->colormap = R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE); diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 6db5d5f08..e26aa98ff 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1363,7 +1363,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr) INT32 skinnum = TC_DEFAULT; if ((spr->mobj->flags & (MF_ENEMY|MF_BOSS)) && (spr->mobj->flags2 & MF2_FRET) && !(spr->mobj->flags & MF_GRENADEBOUNCE) && (leveltime & 1)) // Bosses "flash" { - if (spr->mobj->type == MT_CYBRAKDEMON) + if (spr->mobj->type == MT_CYBRAKDEMON || spr->mobj->colorized) skinnum = TC_ALLWHITE; else if (spr->mobj->type == MT_METALSONIC_BATTLE) skinnum = TC_METALSONIC; diff --git a/src/info.c b/src/info.c index ed990f8b9..0e4511ede 100644 --- a/src/info.c +++ b/src/info.c @@ -1747,20 +1747,23 @@ state_t states[NUMSTATES] = {SPR_METL, 9, 2, {NULL}, 0, 0, S_METALSONIC_RUN1}, // S_METALSONIC_RUN4 {SPR_METL, 4, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_FLOAT - {SPR_METL, 12, -1, {NULL}, 0, 0, S_METALSONIC_STUN}, // S_METALSONIC_VECTOR - {SPR_METL, 0, -1, {NULL}, 0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_STUN - {SPR_METL, 13, 40, {NULL}, 0, 0, S_METALSONIC_GATHER},// S_METALSONIC_RAISE + {SPR_METL, 12|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_METALSONIC_STUN}, // S_METALSONIC_VECTOR + {SPR_METL, 11, -1, {NULL}, 0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_STUN + {SPR_METL, 13, 20, {NULL}, 0, 0, S_METALSONIC_GATHER},// S_METALSONIC_RAISE {SPR_METL, 14, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_GATHER {SPR_METL, 15, -1, {NULL}, 0, 0, S_METALSONIC_BOUNCE},// S_METALSONIC_DASH {SPR_METL, 14, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_BOUNCE {SPR_METL, 16, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_BADBOUNCE {SPR_METL, 13, -1, {NULL}, 0, 0, S_METALSONIC_GATHER},// S_METALSONIC_SHOOT {SPR_METL, 11, 40, {A_Pain}, 0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_PAIN - {SPR_METL, 11, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_METALSONIC_DEATH - {SPR_METL, 3, 4, {NULL}, 0, 0, S_METALSONIC_FLEE2}, // S_METALSONIC_FLEE1 - {SPR_METL, 4, 4, {A_BossScream}, 0, 0, S_METALSONIC_FLEE3}, // S_METALSONIC_FLEE2 - {SPR_METL, 5, 4, {NULL}, 0, 0, S_METALSONIC_FLEE4}, // S_METALSONIC_FLEE3 - {SPR_METL, 4, 4, {NULL}, 0, 0, S_METALSONIC_FLEE1}, // S_METALSONIC_FLEE4 + {SPR_METL, 13, 8, {A_Fall}, 0, 0, S_METALSONIC_DEATH2},// S_METALSONIC_DEATH1 + {SPR_METL, 13, 8, {A_BossScream}, 0, 0, S_METALSONIC_DEATH3},// S_METALSONIC_DEATH2 + {SPR_METL, 13, 0, {A_Repeat}, 11, S_METALSONIC_DEATH2, S_METALSONIC_DEATH4}, // S_METALSONIC_DEATH3 + {SPR_METL, 13, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_METALSONIC_DEATH4 + {SPR_METL, 11, 4, {NULL}, 0, 0, S_METALSONIC_FLEE2}, // S_METALSONIC_FLEE1 + {SPR_METL, 11, 4, {A_BossScream}, 0, 0, S_METALSONIC_FLEE3}, // S_METALSONIC_FLEE2 + {SPR_METL, 11, 4, {NULL}, 0, 0, S_METALSONIC_FLEE4}, // S_METALSONIC_FLEE3 + {SPR_METL, 11, 4, {NULL}, 0, 0, S_METALSONIC_FLEE1}, // S_METALSONIC_FLEE4 {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 0, 1, {NULL}, 0, 0, S_MSSHIELD_F2}, // S_MSSHIELD_F1 {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30| 1, 1, {NULL}, 0, 0, S_MSSHIELD_F3}, // S_MSSHIELD_F2 @@ -6259,13 +6262,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_METALSONIC_DASH, // seestate sfx_s3k54, // seesound 0, // reactiontime - sfx_trpowr, // attacksound + sfx_bechrg, // attacksound S_METALSONIC_PAIN, // painstate S_METALSONIC_VECTOR,// painchance sfx_dmpain, // painsound S_METALSONIC_BADBOUNCE, // meleestate S_METALSONIC_SHOOT, // missilestate - S_METALSONIC_DEATH, // deathstate + S_METALSONIC_DEATH1,// deathstate S_METALSONIC_FLEE1, // xdeathstate sfx_s3k6e, // deathsound MT_ENERGYBALL, // speed @@ -6297,7 +6300,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 0, // speed 32*FRACUNIT, // radius - 64*FRACUNIT, // height + 52*FRACUNIT, // height 0, // display offset 0, // mass 0, // damage @@ -9140,7 +9143,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_ENERGYBALL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate - sfx_s3k54, // seesound + sfx_bexpld, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate diff --git a/src/info.h b/src/info.h index a7f5d775a..c1891a766 100644 --- a/src/info.h +++ b/src/info.h @@ -1888,7 +1888,10 @@ typedef enum state S_METALSONIC_BADBOUNCE, S_METALSONIC_SHOOT, S_METALSONIC_PAIN, - S_METALSONIC_DEATH, + S_METALSONIC_DEATH1, + S_METALSONIC_DEATH2, + S_METALSONIC_DEATH3, + S_METALSONIC_DEATH4, S_METALSONIC_FLEE1, S_METALSONIC_FLEE2, S_METALSONIC_FLEE3, diff --git a/src/lua_baselib.c b/src/lua_baselib.c index e6119cd6c..81a17ef20 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1235,8 +1235,8 @@ static int lib_pHomingAttack(lua_State *L) INLEVEL if (!source || !enemy) return LUA_ErrInvalid(L, "mobj_t"); - P_HomingAttack(source, enemy); - return 0; + lua_pushboolean(L, P_HomingAttack(source, enemy)); + return 1; } static int lib_pSuperReady(lua_State *L) diff --git a/src/p_local.h b/src/p_local.h index bda6133d5..5216286c0 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -182,7 +182,7 @@ void P_InstaThrustEvenIn2D(mobj_t *mo, angle_t angle, fixed_t move); mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet); void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius); -void P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user +boolean P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user boolean P_SuperReady(player_t *player); void P_DoJump(player_t *player, boolean soundandstate); #if 0 diff --git a/src/p_map.c b/src/p_map.c index 2364e8107..e78dd1e84 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -725,6 +725,27 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; } + // vectorise metal - done in a special case as at this point neither has the right flags for touching + if (thing->type == MT_METALSONIC_BATTLE + && (tmthing->flags & MF_MISSILE) + && tmthing->target != thing + && thing->state == &states[thing->info->spawnstate]) + { + blockdist = thing->radius + tmthing->radius; + + if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) + return true; // didn't hit it + + if (tmthing->z > thing->z + thing->height) + return true; // overhead + if (tmthing->z + tmthing->height < thing->z) + return true; // underneath + + thing->flags2 |= MF2_CLASSICPUSH; + + return true; + } + if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) return true; diff --git a/src/p_mobj.c b/src/p_mobj.c index c9dcff210..2e57d6a9c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5377,7 +5377,8 @@ static void P_Boss7Thinker(mobj_t *mobj) if (mobj->info->activesound)\ S_StartSound(mobj, mobj->info->activesound);\ if (mobj->info->painchance)\ - P_SetMobjState(mobj, mobj->info->painchance) + P_SetMobjState(mobj, mobj->info->painchance);\ + mobj->flags2 &= ~MF2_INVERTAIMABLE;\ // Metal Sonic battle boss // You CAN put multiple Metal Sonics in a single map @@ -5464,25 +5465,16 @@ static void P_Boss9Thinker(mobj_t *mobj) // AI goes here. { angle_t angle; - if (mobj->threshold) + if (mobj->threshold || mobj->movecount) mobj->momz = (mobj->watertop-mobj->z)/16; // Float to your desired position FASTER else mobj->momz = (mobj->watertop-mobj->z)/40; // Float to your desired position - if (mobj->movecount == 2) { + if (mobj->movecount == 2) + { mobj_t *spawner; fixed_t dist = 0; - angle = 0x06000000*leveltime; - - // Alter your energy bubble's size/position - if (mobj->health > 3) { - mobj->tracer->destscale = FRACUNIT + (4*TICRATE - mobj->fuse)*(FRACUNIT/2)/TICRATE + FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT),FRACUNIT/2); - P_SetScale(mobj->tracer, mobj->tracer->destscale); - P_TeleportMove(mobj->tracer, mobj->x, mobj->y, mobj->z + mobj->height/2 - mobj->tracer->height/2); - mobj->tracer->momx = mobj->momx; - mobj->tracer->momy = mobj->momy; - mobj->tracer->momz = mobj->momz; - } + angle = 0x06000000*leveltime; // wtf? // Face your target P_BossTargetPlayer(mobj, true); @@ -5493,27 +5485,150 @@ static void P_Boss9Thinker(mobj_t *mobj) else mobj->angle -= InvAngle(angle)/8; + // Alter your energy bubble's size/position + if (mobj->health > 3) + { + mobj->tracer->destscale = FRACUNIT + (4*TICRATE - mobj->fuse)*(FRACUNIT/2)/TICRATE + FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT),FRACUNIT/2); + P_SetScale(mobj->tracer, mobj->tracer->destscale); + } + else + mobj->tracer->frame &= ~FF_TRANSMASK; // this causes a flicker but honestly i like it this way + P_TeleportMove(mobj->tracer, mobj->x, mobj->y, mobj->z + mobj->height/2 - mobj->tracer->height/2); + mobj->tracer->momx = mobj->momx; + mobj->tracer->momy = mobj->momy; + mobj->tracer->momz = mobj->momz; + + // Firin' mah lazors - INDICATOR + if (mobj->fuse > TICRATE/2) + { + tic_t shoottime, worktime, calctime; + shoottime = (TICRATE/((mobj->extravalue1 == 3) ? 8 : 4)); + shoottime += (shoottime>>1); + worktime = shoottime*(mobj->threshold/2); + calctime = mobj->fuse-(TICRATE/2); + + if (calctime <= worktime && (calctime % shoottime == 0)) + { + mobj_t *missile; + + missile = P_SpawnMissile(mobj, mobj->target, MT_MSGATHER); + S_StopSound(missile); + if (mobj->extravalue1 >= 2) + P_SetScale(missile, FRACUNIT>>1); + missile->destscale = missile->scale>>1; + missile->fuse = TICRATE/2; + missile->scalespeed = abs(missile->destscale - missile->scale)/missile->fuse; + missile->z -= missile->height/2; + missile->momx *= -1; + missile->momy *= -1; + missile->momz *= -1; + + if (mobj->extravalue1 == 2) + { + UINT8 i; + mobj_t *spread; + for (i = 0; i < 5; i++) + { + if (i == 2) + continue; + spread = P_SpawnMobj(missile->x, missile->y, missile->z, missile->type); + spread->angle = missile->angle+(ANGLE_11hh/2)*(i-2); + P_InstaThrust(spread,spread->angle,-spread->info->speed); + spread->momz = missile->momz; + P_SetScale(spread, missile->scale); + spread->destscale = missile->destscale; + spread->scalespeed = missile->scalespeed; + spread->fuse = missile->fuse; + P_UnsetThingPosition(spread); + spread->x -= spread->fuse*spread->momx; + spread->y -= spread->fuse*spread->momy; + spread->z -= spread->fuse*spread->momz; + P_SetThingPosition(spread); + } + P_InstaThrust(missile,missile->angle,-missile->info->speed); + } + else if (mobj->extravalue1 >= 3) + { + UINT8 i; + mobj_t *spread; + mobj->target->z -= (4*missile->height); + for (i = 0; i < 5; i++) + { + if (i != 2) + { + spread = P_SpawnMissile(mobj, mobj->target, missile->type); + P_SetScale(spread, missile->scale); + spread->destscale = missile->destscale; + spread->fuse = missile->fuse; + spread->z -= spread->height/2; + spread->momx *= -1; + spread->momy *= -1; + spread->momz *= -1; + P_UnsetThingPosition(spread); + spread->x -= spread->fuse*spread->momx; + spread->y -= spread->fuse*spread->momy; + spread->z -= spread->fuse*spread->momz; + P_SetThingPosition(spread); + } + mobj->target->z += missile->height*2; + } + mobj->target->z -= (6*missile->height); + } + + P_UnsetThingPosition(missile); + missile->x -= missile->fuse*missile->momx; + missile->y -= missile->fuse*missile->momy; + missile->z -= missile->fuse*missile->momz; + P_SetThingPosition(missile); + + S_StartSound(mobj, sfx_s3kb3); + } + } + + // up... + mobj->z += mobj->height/2; + // Spawn energy particles - for (spawner = mobj->hnext; spawner; spawner = spawner->hnext) { + for (spawner = mobj->hnext; spawner; spawner = spawner->hnext) + { dist = P_AproxDistance(spawner->x - mobj->x, spawner->y - mobj->y); if (P_RandomRange(1,(dist>>FRACBITS)/16) == 1) break; } - if (spawner) { + if (spawner) + { mobj_t *missile = P_SpawnMissile(spawner, mobj, MT_MSGATHER); - if (mobj->health > mobj->info->damage) - missile->momz = FixedDiv(missile->momz, 7*FRACUNIT/5); + if (dist == 0) missile->fuse = 0; else missile->fuse = (dist/P_AproxDistance(missile->momx, missile->momy)); + if (missile->fuse > mobj->fuse) P_RemoveMobj(missile); + + if (mobj->health > mobj->info->damage) + { + P_SetScale(missile, FRACUNIT/2); + missile->color = SKINCOLOR_GOLD; // sonic cd electric power + } + else + { + P_SetScale(missile, FRACUNIT/4); + missile->color = SKINCOLOR_MAGENTA; // sonic OVA/4 purple power + } + missile->destscale = missile->scale*2; + missile->scalespeed = abs(missile->scale - missile->destscale)/missile->fuse; + missile->colorized = true; } + + // ...then down. easier than changing the missile's momz after-the-fact + mobj->z -= mobj->height/2; } // Pre-threshold reactiontime stuff for attack phases - if (mobj->reactiontime && mobj->movecount == 3) { + if (mobj->reactiontime && mobj->movecount == 3) + { mobj->reactiontime--; if (mobj->movedir == 0 || mobj->movedir == 2) { // Pausing between bounces in the pinball phase @@ -5534,13 +5649,15 @@ static void P_Boss9Thinker(mobj_t *mobj) } // threshold is used for attacks/maneuvers. - if (mobj->threshold) { + if (mobj->threshold && mobj->movecount != 2) { fixed_t speed = 20*FRACUNIT + FixedMul(40*FRACUNIT, FixedDiv((mobj->info->spawnhealth - mobj->health)<info->spawnhealth<movecount == 3 && mobj->movedir == 1) { - if (!(mobj->threshold&1)) { + if (mobj->movecount == 3 && mobj->movedir == 1) + { + if (!(mobj->threshold & 1)) + { mobj_t *missile; if (mobj->info->seesound) S_StartSound(mobj, mobj->info->seesound); @@ -5552,18 +5669,20 @@ static void P_Boss9Thinker(mobj_t *mobj) A_FaceTarget(mobj); missile = P_SpawnMissile(mobj, mobj->target, mobj->info->speed); - if (mobj->extravalue1 == 2 || mobj->extravalue1 == 3) { + if (mobj->extravalue1 >= 2) + { missile->destscale = FRACUNIT>>1; P_SetScale(missile, missile->destscale); } missile->fuse = 3*TICRATE; missile->z -= missile->height/2; - if (mobj->extravalue1 == 2) { - int i; + if (mobj->extravalue1 == 2) + { + UINT8 i; mobj_t *spread; - missile->flags |= MF_MISSILE; - for (i = 0; i < 5; i++) { + for (i = 0; i < 5; i++) + { if (i == 2) continue; spread = P_SpawnMobj(missile->x, missile->y, missile->z, missile->type); @@ -5572,11 +5691,32 @@ static void P_Boss9Thinker(mobj_t *mobj) spread->momz = missile->momz; spread->destscale = FRACUNIT>>1; P_SetScale(spread, spread->destscale); - spread->fuse = 3*TICRATE; + spread->fuse = missile->fuse; } - missile->flags &= ~MF_MISSILE; + P_InstaThrust(missile,missile->angle,missile->info->speed); } - } else { + else if (mobj->extravalue1 >= 3) + { + UINT8 i; + mobj_t *spread; + mobj->target->z -= (2*missile->height); + for (i = 0; i < 5; i++) + { + if (i != 2) + { + spread = P_SpawnMissile(mobj, mobj->target, missile->type); + spread->destscale = FRACUNIT>>1; + P_SetScale(spread, spread->destscale); + spread->fuse = missile->fuse; + spread->z -= spread->height/2; + } + mobj->target->z += missile->height; + } + mobj->target->z -= (3*missile->height); + } + } + else + { P_SetMobjState(mobj, mobj->state->nextstate); if (mobj->extravalue1 == 3) mobj->reactiontime = TICRATE/8; @@ -5590,7 +5730,8 @@ static void P_Boss9Thinker(mobj_t *mobj) P_SpawnGhostMobj(mobj); // Pinball attack! - if (mobj->movecount == 3 && (mobj->movedir == 0 || mobj->movedir == 2)) { + if (mobj->movecount == 3 && (mobj->movedir == 0 || mobj->movedir == 2)) + { if ((statenum_t)(mobj->state-states) != mobj->info->seestate) P_SetMobjState(mobj, mobj->info->seestate); if (mobj->movedir == 0) // mobj health == 1 @@ -5599,7 +5740,8 @@ static void P_Boss9Thinker(mobj_t *mobj) P_InstaThrust(mobj, mobj->angle, 22*FRACUNIT); else // mobj health == 2 P_InstaThrust(mobj, mobj->angle, 30*FRACUNIT); - if (!P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true)) { // Hit a wall? Find a direction to bounce + if (!P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true)) + { // Hit a wall? Find a direction to bounce mobj->threshold--; P_SetMobjState(mobj, mobj->state->nextstate); if (!mobj->threshold) { // failed bounce! @@ -5612,11 +5754,15 @@ static void P_Boss9Thinker(mobj_t *mobj) mobj->movecount = 0; P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_CYBRAKDEMON_VILE_EXPLOSION); P_SetMobjState(mobj, mobj->info->meleestate); - } else if (!(mobj->threshold%4)) { // We've decided to lock onto the player this bounce. + } + else if (!(mobj->threshold%4)) + { // We've decided to lock onto the player this bounce. S_StartSound(mobj, sfx_s3k5a); mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x + mobj->target->momx*4, mobj->target->y + mobj->target->momy*4); mobj->reactiontime = TICRATE - 5*(mobj->info->damage - mobj->health); // targetting time - } else { // No homing, just use P_BounceMove + } + else + { // No homing, just use P_BounceMove S_StartSound(mobj, sfx_s3kaa); // make the bounces distinct... P_BounceMove(mobj); mobj->angle = R_PointToAngle2(0,0,mobj->momx,mobj->momy); @@ -5630,7 +5776,8 @@ static void P_Boss9Thinker(mobj_t *mobj) // Vector form dodge! mobj->angle += mobj->movedir; P_InstaThrust(mobj, mobj->angle, -speed); - while (!P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true) && tries++ < 16) { + while (!P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true) && tries++ < 16) + { S_StartSound(mobj, sfx_mspogo); P_BounceMove(mobj); mobj->angle = R_PointToAngle2(mobj->momx, mobj->momy,0,0); @@ -5687,7 +5834,7 @@ static void P_Boss9Thinker(mobj_t *mobj) if (mobj->flags2 & MF2_FRET) return; - if (mobj->state == &states[mobj->info->raisestate]) + if (mobj->movecount == 1 || mobj->movecount == 2) { // Charging energy if (mobj->momx != 0 || mobj->momy != 0) { // Apply the air breaks if (abs(mobj->momx)+abs(mobj->momy) < FRACUNIT) @@ -5695,11 +5842,13 @@ static void P_Boss9Thinker(mobj_t *mobj) else P_Thrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), -6*FRACUNIT/8); } - return; + if (mobj->state == states+mobj->info->raisestate) + return; } if (mobj->fuse == 0) { + mobj->flags2 &= ~MF2_INVERTAIMABLE; // It's time to attack! What are we gonna do?! switch(mobj->movecount) { @@ -5707,6 +5856,7 @@ static void P_Boss9Thinker(mobj_t *mobj) default: // Fly up and prepare for an attack! // We have to charge up first, so let's go up into the air + S_StartSound(mobj, sfx_beflap); P_SetMobjState(mobj, mobj->info->raisestate); if (mobj->floorz >= mobj->target->floorz) mobj->watertop = mobj->floorz + 256*FRACUNIT; @@ -5714,33 +5864,69 @@ static void P_Boss9Thinker(mobj_t *mobj) mobj->watertop = mobj->target->floorz + 256*FRACUNIT; break; - case 1: { + case 1: // Okay, we're up? Good, time to gather energy... if (mobj->health > mobj->info->damage) { // No more bubble if we're broken (pinch phase) mobj_t *shield = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_MSSHIELD_FRONT); P_SetTarget(&mobj->tracer, shield); P_SetTarget(&shield->target, mobj); + + // Attack 2: Energy shot! + switch (mobj->health) + { + case 8: // shoot once + default: + mobj->extravalue1 = 0; + mobj->threshold = 2; + break; + case 7: // spread shot (vertical) + mobj->extravalue1 = 4; + mobj->threshold = 2; + break; + case 6: // three shots + mobj->extravalue1 = 1; + mobj->threshold = 3*2; + break; + case 5: // spread shot (horizontal) + mobj->extravalue1 = 2; + mobj->threshold = 2; + break; + case 4: // machine gun + mobj->extravalue1 = 3; + mobj->threshold = 5*2; + break; + } } else - P_LinedefExecute(LE_PINCHPHASE, mobj, NULL); + { + mobj_t *shield = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_MSSHIELD_FRONT); + P_SetTarget(&mobj->tracer, shield); + P_SetTarget(&shield->target, mobj); + shield->height -= 20*FRACUNIT; // different offset... + shield->color = SKINCOLOR_MAGENTA; + shield->colorized = true; + P_SetMobjState(shield, S_FIRS1); + //P_LinedefExecute(LE_PINCHPHASE, mobj, NULL); -- why does this happen twice? see case 2... + } mobj->fuse = 4*TICRATE; mobj->flags |= MF_PAIN; if (mobj->info->attacksound) S_StartSound(mobj, mobj->info->attacksound); A_FaceTarget(mobj); + break; - } case 2: // We're all charged and ready now! Unleash the fury!! - if (mobj->health > mobj->info->damage) + S_StopSound(mobj); + mobj_t *removemobj = mobj->tracer; + P_SetTarget(&mobj->tracer, mobj->hnext); + P_RemoveMobj(removemobj); + if (mobj->health <= mobj->info->damage) { - mobj_t *removemobj = mobj->tracer; - P_SetTarget(&mobj->tracer, mobj->hnext); - P_RemoveMobj(removemobj); - } - if (mobj->health <= mobj->info->damage) { + mobj_t *whoosh; + // Attack 1: Pinball dash! if (mobj->health == 1) mobj->movedir = 0; @@ -5755,32 +5941,23 @@ static void P_Boss9Thinker(mobj_t *mobj) mobj->threshold = 24; // bounce 24 times mobj->watertop = mobj->target->floorz + 16*FRACUNIT; P_LinedefExecute(LE_PINCHPHASE, mobj, NULL); - } else { + + whoosh = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_GHOST); // done here so the offset is correct + whoosh->frame = FF_FULLBRIGHT; + whoosh->sprite = SPR_ARMA; + whoosh->destscale = whoosh->scale<<1; + whoosh->scalespeed = FixedMul(whoosh->scalespeed, whoosh->scale); + whoosh->height = 38*whoosh->scale; + whoosh->fuse = 10; + whoosh->color = SKINCOLOR_MAGENTA; + whoosh->colorized = true; + whoosh->flags |= MF_NOCLIPHEIGHT; + } + else + { // Attack 2: Energy shot! mobj->movedir = 1; - - if (mobj->health >= 8) - mobj->extravalue1 = 0; - else if (mobj->health >= 5) - mobj->extravalue1 = 2; - else if (mobj->health >= 4) - mobj->extravalue1 = 1; - else - mobj->extravalue1 = 3; - - switch(mobj->extravalue1) { - case 0: // shoot once - case 2: // spread-shot - default: - mobj->threshold = 2; - break; - case 1: // shoot 3 times - mobj->threshold = 3*2; - break; - case 3: // shoot like a goddamn machinegun - mobj->threshold = 8*2; - break; - } + // looking for the number of things to fire? that's done in case 1 now } break; @@ -5814,21 +5991,26 @@ static void P_Boss9Thinker(mobj_t *mobj) mobj->angle -= InvAngle(angle)/8; //A_FaceTarget(mobj); - // Check if we're being attacked - if (!mobj->target || !mobj->target->player || !P_PlayerCanDamage(mobj->target->player, mobj)) - goto nodanger; - if (mobj->target->x+mobj->target->radius+abs(mobj->target->momx*2) < mobj->x-mobj->radius) - goto nodanger; - if (mobj->target->x-mobj->target->radius-abs(mobj->target->momx*2) > mobj->x+mobj->radius) - goto nodanger; - if (mobj->target->y+mobj->target->radius+abs(mobj->target->momy*2) < mobj->y-mobj->radius) - goto nodanger; - if (mobj->target->y-mobj->target->radius-abs(mobj->target->momy*2) > mobj->y+mobj->radius) - goto nodanger; - if (mobj->target->z+mobj->target->height+mobj->target->momz*2 < mobj->z) - goto nodanger; - if (mobj->target->z+mobj->target->momz*2 > mobj->z+mobj->height) - goto nodanger; + if (mobj->flags2 & MF2_CLASSICPUSH) + mobj->flags2 &= ~MF2_CLASSICPUSH; // a missile caught us in PIT_CheckThing! + else + { + // Check if we're being attacked + if (!mobj->target || !mobj->target->player || !P_PlayerCanDamage(mobj->target->player, mobj)) + goto nodanger; + if (mobj->target->x+mobj->target->radius+abs(mobj->target->momx*2) < mobj->x-mobj->radius) + goto nodanger; + if (mobj->target->x-mobj->target->radius-abs(mobj->target->momx*2) > mobj->x+mobj->radius) + goto nodanger; + if (mobj->target->y+mobj->target->radius+abs(mobj->target->momy*2) < mobj->y-mobj->radius) + goto nodanger; + if (mobj->target->y-mobj->target->radius-abs(mobj->target->momy*2) > mobj->y+mobj->radius) + goto nodanger; + if (mobj->target->z+mobj->target->height+mobj->target->momz*2 < mobj->z) + goto nodanger; + if (mobj->target->z+mobj->target->momz*2 > mobj->z+mobj->height) + goto nodanger; + } // An incoming attack is detected! What should we do?! // Go into vector form! @@ -5836,13 +6018,17 @@ static void P_Boss9Thinker(mobj_t *mobj) return; nodanger: + mobj->flags2 |= MF2_INVERTAIMABLE; + // Move normally: Approach the player using normal thrust and simulated friction. dist = P_AproxDistance(mobj->x-mobj->target->x, mobj->y-mobj->target->y); P_Thrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), -3*FRACUNIT/8); - if (dist < 64*FRACUNIT) + if (dist < 64*FRACUNIT && !(mobj->target->player && mobj->target->player->homing)) P_Thrust(mobj, mobj->angle, -4*FRACUNIT); else if (dist > 180*FRACUNIT) P_Thrust(mobj, mobj->angle, FRACUNIT); + else + P_Thrust(mobj, mobj->angle + ANGLE_90, FINECOSINE((((angle_t)(leveltime*ANG1))>>ANGLETOFINESHIFT) & FINEMASK)>>1); mobj->momz += P_AproxDistance(mobj->momx, mobj->momy)/12; // Move up higher the faster you're going. } } diff --git a/src/p_user.c b/src/p_user.c index 1294b27d9..3e464d0cf 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4779,10 +4779,10 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) player->mo->momx /= 2; player->mo->momy /= 2; } - else if (player->charability == CA_HOMINGTHOK) + if (player->charability == CA_HOMINGTHOK) { - player->mo->momx /= 3; - player->mo->momy /= 3; + player->mo->momx /= 2; + player->mo->momy /= 2; } if (player->charability == CA_HOMINGTHOK) @@ -7884,7 +7884,7 @@ static void P_MovePlayer(player_t *player) if (!(player->pflags & (PF_USEDOWN|PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY)) // If the player is not holding down BT_USE, or having used an ability previously && (!(player->powers[pw_shield] & SH_NOSTACK) || !(player->pflags & PF_THOKKED) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP && player->secondjump == UINT8_MAX))) // thokked is optional if you're bubblewrapped/turning super { - // Force shield activation + // Force stop if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE) { player->pflags |= PF_THOKKED|PF_SHIELDABILITY; @@ -7900,17 +7900,17 @@ static void P_MovePlayer(player_t *player) if (P_SuperReady(player)) P_DoSuperTransformation(player, false); break; - // Whirlwind/Thundercoin shield activation + // Whirlwind jump/Thunder jump case SH_WHIRLWIND: case SH_THUNDERCOIN: P_DoJumpShield(player); break; - // Armageddon shield activation + // Armageddon pow case SH_ARMAGEDDON: player->pflags |= PF_THOKKED|PF_SHIELDABILITY; P_BlackOw(player); break; - // Attract shield activation + // Attraction blast case SH_ATTRACT: player->pflags |= PF_THOKKED|PF_SHIELDABILITY; player->homing = 2; @@ -7926,7 +7926,7 @@ static void P_MovePlayer(player_t *player) else S_StartSound(player->mo, sfx_s3ka6); break; - // Elemental/Bubblewrap shield activation + // Elemental stomp/Bubble bounce case SH_ELEMENTAL: case SH_BUBBLEWRAP: player->pflags |= PF_THOKKED|PF_SHIELDABILITY; @@ -7940,7 +7940,7 @@ static void P_MovePlayer(player_t *player) ? sfx_s3k43 : sfx_s3k44); break; - // Flame shield activation + // Flame burst case SH_FLAMEAURA: player->pflags |= PF_THOKKED|PF_SHIELDABILITY; P_Thrust(player->mo, player->mo->angle, FixedMul(30*FRACUNIT - FixedSqrt(FixedDiv(player->speed, player->mo->scale)), player->mo->scale)); @@ -7961,8 +7961,7 @@ static void P_MovePlayer(player_t *player) { if (player->homing && player->mo->tracer) { - P_HomingAttack(player->mo, player->mo->tracer); - if (player->mo->tracer->health <= 0 || (player->mo->tracer->flags2 & MF2_FRET)) + if (!P_HomingAttack(player->mo, player->mo->tracer)) { P_SetObjectMomZ(player->mo, 6*FRACUNIT, false); if (player->mo->eflags & MFE_UNDERWATER) @@ -7981,10 +7980,9 @@ static void P_MovePlayer(player_t *player) if (player->homing && player->mo->tracer) { P_SpawnThokMobj(player); - P_HomingAttack(player->mo, player->mo->tracer); // But if you don't, then stop homing. - if (player->mo->tracer->health <= 0 || (player->mo->tracer->flags2 & MF2_FRET)) + if (!P_HomingAttack(player->mo, player->mo->tracer)) { if (player->mo->eflags & MFE_UNDERWATER) P_SetObjectMomZ(player->mo, FixedDiv(457*FRACUNIT,72*FRACUNIT), false); @@ -8564,7 +8562,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next) { mo = (mobj_t *)think; - if (!(mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR|MF_SPRING)) == !(mo->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag + if (!((mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR) && (mo->flags & MF_SHOOTABLE)) || (mo->flags & MF_SPRING)) == !(mo->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag continue; // not a valid target if (mo->health <= 0) // dead @@ -8576,9 +8574,6 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) if (mo->flags2 & MF2_FRET) continue; - if ((mo->flags & (MF_ENEMY|MF_BOSS)) && !(mo->flags & MF_SHOOTABLE)) // don't aim at something you can't shoot at anyway (see Egg Guard or Minus) - continue; - if (!nonenemies && mo->flags & (MF_MONITOR|MF_SPRING)) continue; @@ -8632,17 +8627,23 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) return closestmo; } -void P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target +boolean P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target { fixed_t zdist; fixed_t dist; fixed_t ns = 0; if (!enemy) - return; + return false; - if (!(enemy->health)) - return; + if (!enemy->health) + return false; + + if (enemy->flags2 & MF2_FRET) + return false; + + if (!(enemy->flags & (MF_SHOOTABLE|MF_SPRING)) == !(enemy->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag + return false; // change angle source->angle = R_PointToAngle2(source->x, source->y, enemy->x, enemy->y); @@ -8685,6 +8686,8 @@ void P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target source->momx = FixedMul(FixedDiv(enemy->x - source->x, dist), ns); source->momy = FixedMul(FixedDiv(enemy->y - source->y, dist), ns); source->momz = FixedMul(FixedDiv(zdist, dist), ns); + + return true; } // Search for emeralds diff --git a/src/r_draw.c b/src/r_draw.c index 77bb1b6e7..f8e435624 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -528,12 +528,9 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U || color == SKINCOLOR_NONE) { if (skinnum == TC_ALLWHITE) - memset(dest_colormap, 0, NUM_PALETTE_ENTRIES * sizeof(UINT8**)); + memset(dest_colormap, 0, NUM_PALETTE_ENTRIES * sizeof(UINT8)); else if (skinnum == TC_BLINK && color != SKINCOLOR_NONE) - { - for (i = 0; i < NUM_PALETTE_ENTRIES; i++) - dest_colormap[i] = Color_Index[color-1][3]; - } + memset(dest_colormap, Color_Index[color-1][3], NUM_PALETTE_ENTRIES * sizeof(UINT8)); else { for (i = 0; i < NUM_PALETTE_ENTRIES; i++) diff --git a/src/r_things.c b/src/r_things.c index 9fe1e96e4..155d0f83f 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -723,7 +723,7 @@ static void R_DrawVisSprite(vissprite_t *vis) { // translate certain pixels to white colfunc = transcolfunc; - if (vis->mobj->type == MT_CYBRAKDEMON) + if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized) dc_translation = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE); else if (vis->mobj->type == MT_METALSONIC_BATTLE) dc_translation = R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE); diff --git a/src/sounds.c b/src/sounds.c index 52dbee341..aa1c841d6 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -137,7 +137,7 @@ sfxinfo_t S_sfx[NUMSFX] = // Game objects, etc {"appear", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Appearing platform"}, - {"bkpoof", false, 70, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Armageddon explosion"}, + {"bkpoof", false, 70, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Armageddon pow"}, {"bnce1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bounce"}, // Boing! {"bnce2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Scatter"}, // Boing! {"cannon", false, 64, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Powerful shot"}, @@ -304,7 +304,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3k3e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Flame Shield"}, {"s3k3f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bubble Shield"}, {"s3k40", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Attraction blast"}, - {"s3k41", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Lightning Shield"}, + {"s3k41", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Thunder Shield"}, {"s3k42", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Twinspin"}, {"s3k43", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Flame burst"}, {"s3k44", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bubble bounce"},