diff --git a/src/d_player.h b/src/d_player.h index dd0643bd4..0b271ec2f 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -455,6 +455,8 @@ typedef struct player_s UINT8 drilldelay; boolean bonustime; // Capsule destroyed, now it's bonus time! mobj_t *capsule; // Go inside the capsule + mobj_t *drone; // Move center to the drone + fixed_t oldscale; // Pre-Nightserize scale UINT8 mare; // Current mare UINT8 marelap; // Current mare lap UINT8 marebonuslap; // Current mare lap starting from bonus time diff --git a/src/dehacked.c b/src/dehacked.c index 381f7e4fc..11ad022ca 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6408,8 +6408,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_TOAD", // Nights-specific stuff - "S_NIGHTSDRONE1", - "S_NIGHTSDRONE2", + "S_NIGHTSDRONE_MAN1", + "S_NIGHTSDRONE_MAN2", "S_NIGHTSDRONE_SPARKLING1", "S_NIGHTSDRONE_SPARKLING2", "S_NIGHTSDRONE_SPARKLING3", @@ -6426,10 +6426,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_NIGHTSDRONE_SPARKLING14", "S_NIGHTSDRONE_SPARKLING15", "S_NIGHTSDRONE_SPARKLING16", - "S_NIGHTSGOAL1", - "S_NIGHTSGOAL2", - "S_NIGHTSGOAL3", - "S_NIGHTSGOAL4", + "S_NIGHTSDRONE_GOAL1", + "S_NIGHTSDRONE_GOAL2", + "S_NIGHTSDRONE_GOAL3", + "S_NIGHTSDRONE_GOAL4", "S_NIGHTSPARKLE1", "S_NIGHTSPARKLE2", @@ -7300,7 +7300,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_AXISTRANSFER", "MT_AXISTRANSFERLINE", "MT_NIGHTSDRONE", - "MT_NIGHTSGOAL", + "MT_NIGHTSDRONE_MAN", + "MT_NIGHTSDRONE_SPARKLING", + "MT_NIGHTSDRONE_GOAL", "MT_NIGHTSPARKLE", "MT_NIGHTSLOOPHELPER", "MT_NIGHTSBUMPER", // NiGHTS Bumper diff --git a/src/info.c b/src/info.c index 72abcede1..45d7438b9 100644 --- a/src/info.c +++ b/src/info.c @@ -3278,8 +3278,8 @@ state_t states[NUMSTATES] = {SPR_TOAD, 0, -1, {NULL}, 0, 0, S_NULL}, // S_TOAD // Nights Drone - {SPR_NDRN, 0, -1, {NULL}, 0, 0, S_NIGHTSDRONE2}, // S_NIGHTSDRONE1 - {SPR_NDRN, 0, -1, {NULL}, 0, 0, S_NIGHTSDRONE1}, // S_NIGHTSDRONE2 + {SPR_NDRN, 0, -1, {NULL}, 0, 0, S_NIGHTSDRONE_MAN2}, // S_NIGHTSDRONE_MAN1 + {SPR_NDRN, 0, -1, {NULL}, 0, 0, S_NIGHTSDRONE_MAN1}, // S_NIGHTSDRONE_MAN2 // Sparkling point (RETURN TO THE GOAL, etc) {SPR_IVSP, 0, 1, {A_GhostMe}, 0, 0, S_NIGHTSDRONE_SPARKLING2}, // S_NIGHTSDRONE_SPARKLING1 @@ -3300,10 +3300,10 @@ state_t states[NUMSTATES] = {SPR_IVSP, 30, 1, {A_GhostMe}, 0, 0, S_NIGHTSDRONE_SPARKLING1}, // S_NIGHTSDRONE_SPARKLING16 // NiGHTS GOAL banner (inside the sparkles!) - {SPR_GOAL, 0, 4, {NULL}, 0, 0, S_NIGHTSGOAL2}, // S_NIGHTSGOAL1 - {SPR_GOAL, 1, 4, {NULL}, 0, 0, S_NIGHTSGOAL3}, // S_NIGHTSGOAL2 - {SPR_GOAL, 2, 4, {NULL}, 0, 0, S_NIGHTSGOAL4}, // S_NIGHTSGOAL3 - {SPR_GOAL, 3, 4, {NULL}, 0, 0, S_NIGHTSGOAL1}, // S_NIGHTSGOAL4 + {SPR_GOAL, 0, 4, {NULL}, 0, 0, S_NIGHTSDRONE_GOAL2}, // S_NIGHTSDRONE_GOAL1 + {SPR_GOAL, 1, 4, {NULL}, 0, 0, S_NIGHTSDRONE_GOAL3}, // S_NIGHTSDRONE_GOAL2 + {SPR_GOAL, 2, 4, {NULL}, 0, 0, S_NIGHTSDRONE_GOAL4}, // S_NIGHTSDRONE_GOAL3 + {SPR_GOAL, 3, 4, {NULL}, 0, 0, S_NIGHTSDRONE_GOAL1}, // S_NIGHTSDRONE_GOAL4 // Nights Sparkle {SPR_NSPK, FF_FULLBRIGHT, 140, {NULL}, 0, 0, S_NIGHTSPARKLE2}, // S_NIGHTSPARKLE1 @@ -16652,7 +16652,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_NIGHTSDRONE 1703, // doomednum - S_NIGHTSDRONE1, // spawnstate + S_INVISIBLE, // spawnstate 120, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -16668,18 +16668,45 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius - 56*FRACUNIT, // height + 80*FRACUNIT, // height 1, // display offset 1000, // mass 0, // damage sfx_ideya, // activesound - MF_SPECIAL, // flags + MF_NOGRAVITY|MF_NOCLIP|MF_SPECIAL, // flags S_NULL // raisestate }, - { // MT_NIGHTSGOAL + { // MT_NIGHTSDRONE_MAN + -1, // doomednum + S_INVISIBLE, // spawnstate + 120, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 255, // painchance + sfx_None, // painsound + S_NIGHTSDRONE_MAN1, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 56*FRACUNIT, // height + 1, // display offset + 1000, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_NIGHTSDRONE_SPARKLING -1, // doomednum - S_NIGHTSGOAL1, // spawnstate + S_INVISIBLE, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -16688,14 +16715,41 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // painstate 255, // painchance sfx_None, // painsound - S_NULL, // meleestate + S_NIGHTSDRONE_SPARKLING1, // meleestate S_NULL, // missilestate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius - 56*FRACUNIT, // height + 24*FRACUNIT, // height + 1, // display offset + 1000, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_NIGHTSDRONE_GOAL + -1, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 255, // painchance + sfx_None, // painsound + S_NIGHTSDRONE_GOAL1, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 24*FRACUNIT, // height -1, // display offset 1000, // mass 0, // damage diff --git a/src/info.h b/src/info.h index c9de82541..e3af600a3 100644 --- a/src/info.h +++ b/src/info.h @@ -3344,8 +3344,8 @@ typedef enum state S_TOAD, // Nights-specific stuff - S_NIGHTSDRONE1, - S_NIGHTSDRONE2, + S_NIGHTSDRONE_MAN1, + S_NIGHTSDRONE_MAN2, S_NIGHTSDRONE_SPARKLING1, S_NIGHTSDRONE_SPARKLING2, S_NIGHTSDRONE_SPARKLING3, @@ -3362,10 +3362,10 @@ typedef enum state S_NIGHTSDRONE_SPARKLING14, S_NIGHTSDRONE_SPARKLING15, S_NIGHTSDRONE_SPARKLING16, - S_NIGHTSGOAL1, - S_NIGHTSGOAL2, - S_NIGHTSGOAL3, - S_NIGHTSGOAL4, + S_NIGHTSDRONE_GOAL1, + S_NIGHTSDRONE_GOAL2, + S_NIGHTSDRONE_GOAL3, + S_NIGHTSDRONE_GOAL4, S_NIGHTSPARKLE1, S_NIGHTSPARKLE2, @@ -4256,7 +4256,9 @@ typedef enum mobj_type MT_AXISTRANSFER, MT_AXISTRANSFERLINE, MT_NIGHTSDRONE, - MT_NIGHTSGOAL, + MT_NIGHTSDRONE_MAN, + MT_NIGHTSDRONE_SPARKLING, + MT_NIGHTSDRONE_GOAL, MT_NIGHTSPARKLE, MT_NIGHTSLOOPHELPER, MT_NIGHTSBUMPER, // NiGHTS Bumper diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index f973061f1..4919883c4 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -288,6 +288,10 @@ static int player_get(lua_State *L) lua_pushboolean(L, plr->bonustime); else if (fastcmp(field,"capsule")) LUA_PushUserdata(L, plr->capsule, META_MOBJ); + else if (fastcmp(field,"drone")) + LUA_PushUserdata(L, plr->drone, META_MOBJ); + else if (fastcmp(field,"oldscale")) + lua_pushfixed(L, plr->oldscale); else if (fastcmp(field,"mare")) lua_pushinteger(L, plr->mare); else if (fastcmp(field,"marelap")) @@ -586,6 +590,15 @@ static int player_set(lua_State *L) mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); P_SetTarget(&plr->capsule, mo); } + else if (fastcmp(field,"drone")) + { + mobj_t *mo = NULL; + if (!lua_isnil(L, 3)) + mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); + P_SetTarget(&plr->drone, mo); + } + else if (fastcmp(field,"oldscale")) + plr->oldscale = luaL_checkfixed(L, 3); else if (fastcmp(field,"mare")) plr->mare = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"marelap")) diff --git a/src/p_enemy.c b/src/p_enemy.c index 6fee7d2f0..d9fc26082 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8553,12 +8553,16 @@ void A_ToggleFlameJet(mobj_t* actor) // Description: Used by Chaos Emeralds to orbit around Nights (aka Super Sonic.) // // var1 = Angle adjustment (aka orbit speed) -// var2 = Lower four bits: height offset, Upper 4 bits = set if object is Nightopian Helper +// var2: +// Lower 16 bits: height offset +// Bits 17-20: set if object is Nightopian Helper +// Bits 21-24: set to not sync scale to player // void A_OrbitNights(mobj_t* actor) { INT32 ofs = (var2 & 0xFFFF); - boolean ishelper = (var2 & 0xFFFF0000); + boolean ishelper = (var2 & 0xF0000); + boolean donotrescale = (var2 & 0xF00000); #ifdef HAVE_BLUA if (LUA_CallAction("A_OrbitNights", actor)) return; @@ -8603,6 +8607,9 @@ void A_OrbitNights(mobj_t* actor) else actor->flags2 &= ~MF2_DONTDRAW; } + + if (!donotrescale && actor->destscale != actor->target->destscale) + actor->destscale = actor->target->destscale; } } diff --git a/src/p_inter.c b/src/p_inter.c index 63abf4c0e..b007de030 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -793,26 +793,33 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_SwitchSpheresBonusMode(false); if (!(netgame || multiplayer) && !(player->powers[pw_carry] == CR_NIGHTSMODE)) P_SetTarget(&special->tracer, toucher); + P_SetTarget(&player->drone, special); // Mark the player as 'center into the drone' P_NightserizePlayer(player, special->health); // Transform! if (!spec) { if (toucher->tracer) // Move the ideya over to the drone! { - mobj_t *hnext = special->hnext; - P_SetTarget(&special->hnext, toucher->tracer); - P_SetTarget(&special->hnext->hnext, hnext); // Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo. - P_SetTarget(&special->hnext->target, special); + mobj_t *orbittarget = special->target ? special->target : special; + mobj_t *hnext = orbittarget->hnext; + + P_SetTarget(&orbittarget->hnext, toucher->tracer); + if (!orbittarget->hnext->hnext) + P_SetTarget(&orbittarget->hnext->hnext, hnext); // Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo. + else + P_SetTarget(&orbittarget->hnext->hnext->target, orbittarget); + P_SetTarget(&orbittarget->hnext->target, orbittarget); P_SetTarget(&toucher->tracer, NULL); + if (hnext) { - special->hnext->extravalue1 = (angle_t)(hnext->extravalue1 - 72*ANG1); - if (special->hnext->extravalue1 > hnext->extravalue1) - special->hnext->extravalue1 -= (72*ANG1)/special->hnext->extravalue1; + orbittarget->hnext->extravalue1 = (angle_t)(hnext->extravalue1 - 72*ANG1); + if (orbittarget->hnext->extravalue1 > hnext->extravalue1) + orbittarget->hnext->extravalue1 -= (72*ANG1)/orbittarget->hnext->extravalue1; } } if (player->exiting) // ...then move it back? { - mobj_t *hnext = special; + mobj_t *hnext = special->target ? special->target : special; // goalpost while ((hnext = hnext->hnext)) P_SetTarget(&hnext->target, toucher); } @@ -961,8 +968,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (player->powers[pw_carry] == CR_NIGHTSMODE && !toucher->target) return; - if (toucher->tracer) - return; // Don't have multiple ideya + if (toucher->tracer && toucher->tracer->health > 0) + return; // Don't have multiple ideya, unless it's the first one given (health = 0) if (player->mare != special->threshold) // wrong mare return; diff --git a/src/p_mobj.c b/src/p_mobj.c index bf7e01d06..16969db59 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7924,78 +7924,247 @@ void P_MobjThinker(mobj_t *mobj) } break; case MT_NIGHTSDRONE: - // GOAL mode? - if (mobj->state >= &states[S_NIGHTSDRONE_SPARKLING1] && mobj->state <= &states[S_NIGHTSDRONE_SPARKLING16]) { - INT32 i; - boolean bonustime = false; + // variable setup + mobj_t *goalpost = NULL; + mobj_t *sparkle = NULL; + mobj_t *droneman = NULL; - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE) - { - bonustime = true; - break; - } + boolean flip = mobj->flags2 & MF2_OBJECTFLIP; + boolean topaligned = (mobj->flags & MF_SLIDEME) && !(mobj->flags & MF_GRENADEBOUNCE); + boolean middlealigned = (mobj->flags & MF_GRENADEBOUNCE) && !(mobj->flags & MF_SLIDEME); + boolean bottomoffsetted = !(mobj->flags & MF_SLIDEME) && !(mobj->flags & MF_GRENADEBOUNCE); + boolean flipchanged = false; - if (!bonustime) + fixed_t dronemanoffset, goaloffset, sparkleoffset, droneboxmandiff, dronemangoaldiff; + + if (mobj->target && mobj->target->type == MT_NIGHTSDRONE_GOAL) { - CONS_Debug(DBG_NIGHTSBASIC, "Removing goal post\n"); - P_RemoveMobj(mobj->target); - P_SetTarget(&mobj->target, NULL); - - mobj->flags &= ~MF_NOGRAVITY; - mobj->flags2 |= MF2_DONTDRAW; - P_SetMobjState(mobj, S_NIGHTSDRONE1); + goalpost = mobj->target; + if (goalpost->target && goalpost->target->type == MT_NIGHTSDRONE_SPARKLING) + sparkle = goalpost->target; + if (goalpost->tracer && goalpost->tracer->type == MT_NIGHTSDRONE_MAN) + droneman = goalpost->tracer; } - } - // Invisible/bouncing mode. - else - { - INT32 i; - boolean bonustime = false; - // Bouncy bouncy! - mobj->angle += ANG10; - if (mobj->flags2 & MF2_DONTDRAW) - mobj->momz = 0; - else if (mobj->z <= mobj->floorz) - mobj->momz = 5*FRACUNIT; + if (!goalpost || !sparkle || !droneman) + break; - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE) - { - bonustime = true; - break; - } + // did NIGHTSDRONE position, scale, flip, or flags change? all elements need to be synced + droneboxmandiff = max(mobj->height - droneman->height, 0); + dronemangoaldiff = max(droneman->height - goalpost->height, 0); - if (bonustime) + if (!(goalpost->flags2 & MF2_OBJECTFLIP) && (mobj->flags2 & MF2_OBJECTFLIP)) { - mobj->z = mobj->floorz + mobj->height; - mobj->angle = mobj->momz = 0; - - if (mobj->spawnpoint) - mobj->z += (mobj->spawnpoint->options >> ZSHIFT)<target, P_SpawnMobjFromMobj(mobj, 0, 0, FRACUNIT, MT_NIGHTSGOAL)); - - mobj->flags2 &= ~MF2_DONTDRAW; - mobj->flags |= MF_NOGRAVITY; - P_SetMobjState(mobj, S_NIGHTSDRONE_SPARKLING1); + goalpost->eflags |= MFE_VERTICALFLIP; + goalpost->flags2 |= MF2_OBJECTFLIP; + sparkle->eflags |= MFE_VERTICALFLIP; + sparkle->flags2 |= MF2_OBJECTFLIP; + droneman->eflags |= MFE_VERTICALFLIP; + droneman->flags2 |= MF2_OBJECTFLIP; + flipchanged = true; } - else if (!G_IsSpecialStage(gamemap)) + else if ((goalpost->flags2 & MF2_OBJECTFLIP) && !(mobj->flags2 & MF2_OBJECTFLIP)) { - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].powers[pw_carry] != CR_NIGHTSMODE) + goalpost->eflags &= ~MFE_VERTICALFLIP; + goalpost->flags2 &= ~MF2_OBJECTFLIP; + sparkle->eflags &= ~MFE_VERTICALFLIP; + sparkle->flags2 &= ~MF2_OBJECTFLIP; + droneman->eflags &= ~MFE_VERTICALFLIP; + droneman->flags2 &= ~MF2_OBJECTFLIP; + flipchanged = true; + } + + if (goalpost->destscale != mobj->destscale + || goalpost->movefactor != mobj->z + || goalpost->friction != mobj->height + || flipchanged + || goalpost->threshold != (mobj->flags & (MF_SLIDEME | MF_GRENADEBOUNCE))) + { + goalpost->destscale = sparkle->destscale = droneman->destscale = mobj->destscale; + + // straight copy-pasta from P_SpawnMapThing, case MT_NIGHTSDRONE + if (!flip) + { + if (topaligned) // Align droneman to top of hitbox { - bonustime = true; // variable reuse + dronemanoffset = droneboxmandiff; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + else if (middlealigned) // Align droneman to center of hitbox + { + dronemanoffset = droneboxmandiff / 2; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + else if (bottomoffsetted) + { + dronemanoffset = 24*FRACUNIT; + goaloffset = dronemangoaldiff + dronemanoffset; + } + else + { + dronemanoffset = 0; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + + sparkleoffset = goaloffset - FixedMul(15*FRACUNIT, mobj->scale); + } + else + { + if (topaligned) // Align droneman to top of hitbox + { + dronemanoffset = 0; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + else if (middlealigned) // Align droneman to center of hitbox + { + dronemanoffset = droneboxmandiff / 2; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + else if (bottomoffsetted) + { + dronemanoffset = droneboxmandiff - FixedMul(24*FRACUNIT, mobj->scale); + goaloffset = dronemangoaldiff + dronemanoffset; + } + else + { + dronemanoffset = droneboxmandiff; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + + sparkleoffset = goaloffset + FixedMul(15*FRACUNIT, mobj->scale); + } + + P_TeleportMove(goalpost, mobj->x, mobj->y, mobj->z + goaloffset); + P_TeleportMove(sparkle, mobj->x, mobj->y, mobj->z + sparkleoffset); + if (goalpost->movefactor != mobj->z || goalpost->friction != mobj->height) + { + P_TeleportMove(droneman, mobj->x, mobj->y, mobj->z + dronemanoffset); + goalpost->movefactor = mobj->z; + goalpost->friction = mobj->height; + } + goalpost->threshold = mobj->flags & (MF_SLIDEME | MF_GRENADEBOUNCE); + } + else + { + if (goalpost->x != mobj->x || goalpost->y != mobj->y) + { + P_TeleportMove(goalpost, mobj->x, mobj->y, goalpost->z); + P_TeleportMove(sparkle, mobj->x, mobj->y, sparkle->z); + } + + if (droneman->x != mobj->x || droneman->y != mobj->y) + P_TeleportMove(droneman, mobj->x, mobj->y, + droneman->z >= mobj->floorz && droneman->z <= mobj->ceilingz ? droneman->z : mobj->z); + } + + // now toggle states! + // GOAL mode? + if (sparkle->state >= &states[S_NIGHTSDRONE_SPARKLING1] && sparkle->state <= &states[S_NIGHTSDRONE_SPARKLING16]) + { + INT32 i; + boolean bonustime = false; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE) + { + bonustime = true; + break; + } + + if (!bonustime) + { + CONS_Debug(DBG_NIGHTSBASIC, "Removing goal post\n"); + if (goalpost && goalpost->state != &states[S_INVISIBLE]) + P_SetMobjState(goalpost, S_INVISIBLE); + if (sparkle && sparkle->state != &states[S_INVISIBLE]) + P_SetMobjState(sparkle, S_INVISIBLE); + } + } + // Invisible/bouncing mode. + else + { + fixed_t droneboxmandiff = max(mobj->height - droneman->height, 0); + INT32 i; + boolean bonustime = false; + fixed_t zcomp; + + // Bouncy bouncy! + if (!flip) + { + if (topaligned) + zcomp = droneboxmandiff + mobj->z; + else if (middlealigned) + zcomp = (droneboxmandiff / 2) + mobj->z; + else if (bottomoffsetted) + zcomp = mobj->z + FixedMul(24*FRACUNIT, mobj->scale); + else + zcomp = mobj->z; + } + else + { + if (topaligned) + zcomp = mobj->z; + else if (middlealigned) + zcomp = (droneboxmandiff / 2) + mobj->z; + else if (bottomoffsetted) + zcomp = mobj->z + droneboxmandiff - FixedMul(24*FRACUNIT, mobj->scale); + else + zcomp = mobj->z + droneboxmandiff; + } + + droneman->angle += ANG10; + if (!flip && droneman->z <= zcomp) + droneman->momz = FixedMul(5*FRACUNIT, droneman->scale); + else if (flip && droneman->z >= zcomp) + droneman->momz = FixedMul(-5*FRACUNIT, droneman->scale); + + // state switching logic + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE) + { + bonustime = true; break; } if (bonustime) - mobj->flags2 &= ~MF2_DONTDRAW; - else - mobj->flags2 |= MF2_DONTDRAW; + { + CONS_Debug(DBG_NIGHTSBASIC, "Adding goal post\n"); + if (!(droneman->flags2 & MF2_DONTDRAW)) + droneman->flags2 |= MF2_DONTDRAW; + if (goalpost->state == &states[S_INVISIBLE]) + P_SetMobjState(goalpost, mobjinfo[goalpost->type].meleestate); + if (sparkle->state == &states[S_INVISIBLE]) + P_SetMobjState(sparkle, mobjinfo[sparkle->type].meleestate); + } + else if (!G_IsSpecialStage(gamemap)) + { + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].powers[pw_carry] != CR_NIGHTSMODE) + { + bonustime = true; // variable reuse + break; + } + + if (bonustime) + { + // show droneman if at least one player is non-nights + if (goalpost->state != &states[S_INVISIBLE]) + P_SetMobjState(goalpost, S_INVISIBLE); + if (sparkle->state != &states[S_INVISIBLE]) + P_SetMobjState(sparkle, S_INVISIBLE); + if (droneman->state != &states[mobjinfo[droneman->type].meleestate]) + P_SetMobjState(droneman, mobjinfo[droneman->type].meleestate); + if (droneman->flags2 & MF2_DONTDRAW) + droneman->flags2 &= ~MF2_DONTDRAW; + } + else + { + // else, hide it + if (!(droneman->flags2 & MF2_DONTDRAW)) + droneman->flags2 |= MF2_DONTDRAW; + } + } } } break; @@ -8850,8 +9019,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->fuse += 30 * TICRATE; break; case MT_NIGHTSDRONE: - if (G_IsSpecialStage(gamemap)) - mobj->flags2 |= MF2_DONTDRAW; nummaprings = -1; // no perfect bonus, rings are free break; case MT_EGGCAPSULE: @@ -9556,6 +9723,7 @@ void P_SpawnPlayer(INT32 playernum) if (p == players) // this is totally the wrong place to do this aaargh. { mobj_t *idya = P_SpawnMobjFromMobj(mobj, 0, 0, mobj->height, MT_GOTEMERALD); + idya->health = 0; // for identification P_SetTarget(&idya->target, mobj); P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate); P_SetTarget(&mobj->tracer, idya); @@ -10625,8 +10793,120 @@ ML_EFFECT4 : Don't clip inside the ground mobj->threshold = mthing->angle >> 8; break; case MT_NIGHTSDRONE: - if (mthing->angle > 0) - mobj->health = mthing->angle; + { + boolean flip = mthing->options & MTF_OBJECTFLIP; + boolean topaligned = (mthing->options & MTF_OBJECTSPECIAL) && !(mthing->options & MTF_EXTRA); + boolean middlealigned = (mthing->options & MTF_EXTRA) && !(mthing->options & MTF_OBJECTSPECIAL); + boolean bottomoffsetted = !(mthing->options & MTF_OBJECTSPECIAL) && !(mthing->options & MTF_EXTRA); + + INT16 timelimit = mthing->angle & 0xFFF; + fixed_t hitboxradius = ((mthing->angle & 0xF000) >> 12) * 32 * FRACUNIT; + fixed_t hitboxheight = mthing->extrainfo * 32 * FRACUNIT; + fixed_t oldheight = mobj->height; + fixed_t dronemanoffset, goaloffset, sparkleoffset, droneboxmandiff, dronemangoaldiff; + + if (timelimit > 0) + mobj->health = timelimit; + + if (hitboxradius > 0) + mobj->radius = hitboxradius; + + if (hitboxheight > 0) + mobj->height = hitboxheight; + else + mobj->height = mobjinfo[MT_NIGHTSDRONE].height; + + droneboxmandiff = max(mobj->height - mobjinfo[MT_NIGHTSDRONE_MAN].height, 0); + dronemangoaldiff = max(mobjinfo[MT_NIGHTSDRONE_MAN].height - mobjinfo[MT_NIGHTSDRONE_GOAL].height, 0); + + if (flip && mobj->height != oldheight) + P_TeleportMove(mobj, mobj->x, mobj->y, mobj->z - (mobj->height - oldheight)); + + if (!flip) + { + if (topaligned) // Align droneman to top of hitbox + { + dronemanoffset = droneboxmandiff; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + else if (middlealigned) // Align droneman to center of hitbox + { + dronemanoffset = droneboxmandiff / 2; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + else if (bottomoffsetted) + { + dronemanoffset = 24*FRACUNIT; + goaloffset = dronemangoaldiff + dronemanoffset; + } + else + { + dronemanoffset = 0; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + + sparkleoffset = goaloffset - FixedMul(15*FRACUNIT, mobj->scale); + } + else + { + mobj->eflags |= MFE_VERTICALFLIP; + mobj->flags2 |= MF2_OBJECTFLIP; + + if (topaligned) // Align droneman to top of hitbox + { + dronemanoffset = 0; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + else if (middlealigned) // Align droneman to center of hitbox + { + dronemanoffset = droneboxmandiff / 2; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + else if (bottomoffsetted) + { + dronemanoffset = droneboxmandiff - FixedMul(24*FRACUNIT, mobj->scale); + goaloffset = dronemangoaldiff + dronemanoffset; + } + else + { + dronemanoffset = droneboxmandiff; + goaloffset = dronemangoaldiff / 2 + dronemanoffset; + } + + sparkleoffset = goaloffset + FixedMul(15*FRACUNIT, mobj->scale); + } + + // spawn visual elements + mobj_t *goalpost = P_SpawnMobjFromMobj(mobj, 0, 0, goaloffset, MT_NIGHTSDRONE_GOAL); + mobj_t *sparkle = P_SpawnMobjFromMobj(mobj, 0, 0, sparkleoffset, MT_NIGHTSDRONE_SPARKLING); + mobj_t *droneman = P_SpawnMobjFromMobj(mobj, 0, 0, dronemanoffset, MT_NIGHTSDRONE_MAN); + + P_SetTarget(&mobj->target, goalpost); + P_SetTarget(&goalpost->target, sparkle); + P_SetTarget(&goalpost->tracer, droneman); + + // correct Z position + if (flip) + { + P_TeleportMove(goalpost, goalpost->x, goalpost->y, mobj->z + goaloffset); + P_TeleportMove(sparkle, sparkle->x, sparkle->y, mobj->z + sparkleoffset); + P_TeleportMove(droneman, droneman->x, droneman->y, mobj->z + dronemanoffset); + } + + // Remember position preference for later + mobj->flags &= ~(MF_SLIDEME | MF_GRENADEBOUNCE); + if (topaligned) + mobj->flags |= MF_SLIDEME; + else if (middlealigned) + mobj->flags |= MF_GRENADEBOUNCE; + else if (!bottomoffsetted) + mobj->flags |= MF_SLIDEME | MF_GRENADEBOUNCE; + + // Remember old Z position and flags for correction detection + goalpost->movefactor = mobj->z; + goalpost->friction = mobj->height; + goalpost->threshold = mobj->flags & (MF_SLIDEME | MF_GRENADEBOUNCE); + } break; case MT_HIVEELEMENTAL: if (mthing->extrainfo) diff --git a/src/p_saveg.c b/src/p_saveg.c index f0a8757c1..10ed68ddf 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -58,6 +58,7 @@ typedef enum FIRSTAXIS = 0x10, SECONDAXIS = 0x20, FOLLOW = 0x40, + DRONE = 0x80, } player_saveflags; // @@ -198,10 +199,10 @@ static void P_NetArchivePlayers(void) WRITEINT32(save_p, players[i].drillmeter); WRITEUINT8(save_p, players[i].drilldelay); WRITEUINT8(save_p, players[i].bonustime); + WRITEFIXED(save_p, players[i].oldscale); WRITEUINT8(save_p, players[i].mare); WRITEUINT8(save_p, players[i].marelap); WRITEUINT8(save_p, players[i].marebonuslap); - WRITEUINT32(save_p, players[i].marebegunat); WRITEUINT32(save_p, players[i].startedtime); WRITEUINT32(save_p, players[i].finishedtime); @@ -236,6 +237,9 @@ static void P_NetArchivePlayers(void) if (players[i].followmobj) flags |= FOLLOW; + if (players[i].drone) + flags |= DRONE; + WRITEINT16(save_p, players[i].lastsidehit); WRITEINT16(save_p, players[i].lastlinehit); @@ -264,6 +268,9 @@ static void P_NetArchivePlayers(void) if (flags & FOLLOW) WRITEUINT32(save_p, players[i].followmobj->mobjnum); + if (flags & DRONE) + WRITEUINT32(save_p, players[i].drone->mobjnum); + WRITEFIXED(save_p, players[i].camerascale); WRITEFIXED(save_p, players[i].shieldscale); @@ -396,10 +403,10 @@ static void P_NetUnArchivePlayers(void) players[i].drillmeter = READINT32(save_p); players[i].drilldelay = READUINT8(save_p); players[i].bonustime = (boolean)READUINT8(save_p); + players[i].oldscale = READFIXED(save_p); players[i].mare = READUINT8(save_p); players[i].marelap = READUINT8(save_p); players[i].marebonuslap = READUINT8(save_p); - players[i].marebegunat = READUINT32(save_p); players[i].startedtime = READUINT32(save_p); players[i].finishedtime = READUINT32(save_p); @@ -447,6 +454,9 @@ static void P_NetUnArchivePlayers(void) if (flags & FOLLOW) players[i].followmobj = (mobj_t *)(size_t)READUINT32(save_p); + if (flags & DRONE) + players[i].drone = (mobj_t *)(size_t)READUINT32(save_p); + players[i].camerascale = READFIXED(save_p); players[i].shieldscale = READFIXED(save_p); @@ -3670,6 +3680,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&mobj->player->followmobj, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "followmobj not found on %d\n", mobj->type); } + if (mobj->player && mobj->player->drone) + { + temp = (UINT32)(size_t)mobj->player->drone; + mobj->player->drone = NULL; + if (!P_SetTarget(&mobj->player->drone, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "drone not found on %d\n", mobj->type); + } } } } diff --git a/src/p_setup.c b/src/p_setup.c index 8d8f184c6..62c4d8e67 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2300,7 +2300,7 @@ static void P_LevelInitStuff(void) players[i].texttimer = players[i].linkcount =\ players[i].linktimer = players[i].flyangle =\ players[i].anotherflyangle = players[i].nightstime =\ - players[i].mare = players[i].marelap =\ + players[i].oldscale = players[i].mare = players[i].marelap =\ players[i].marebonuslap = players[i].lapbegunat =\ players[i].lapstartedtime = players[i].totalmarescore =\ players[i].realtime = players[i].exiting = 0; @@ -2318,7 +2318,7 @@ static void P_LevelInitStuff(void) // unset ALL the pointers. P_SetTarget isn't needed here because if this // function is being called we're just going to clobber the data anyways players[i].mo = players[i].followmobj = players[i].awayviewmobj =\ - players[i].capsule = players[i].axis1 = players[i].axis2 = NULL; + players[i].capsule = players[i].axis1 = players[i].axis2 = players[i].drone = NULL; } } diff --git a/src/p_user.c b/src/p_user.c index 21f9a1813..12a09a23e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -634,6 +634,10 @@ static void P_DeNightserizePlayer(player_t *player) break; } + if (player->mo->scale != player->oldscale) + player->mo->destscale = player->oldscale; + player->oldscale = 0; + // Restore from drowning music P_RestoreMusic(player); @@ -653,7 +657,10 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) return; if (player->powers[pw_carry] != CR_NIGHTSMODE) + { player->mo->height = P_GetPlayerHeight(player); // Just to make sure jumping into the drone doesn't result in a squashed hitbox. + player->oldscale = player->mo->scale; + } player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING); player->homing = 0; @@ -790,6 +797,9 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->texttimer = (UINT8)((110 - 70) - timeinmap); } + if (player->drone && player->drone->scale != player->mo->scale) + player->mo->destscale = player->drone->scale; + // force NiGHTS to face forward or backward if (player->mo->target) player->mo->angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y) // player->angle_pos, won't be set on first instance @@ -6148,6 +6158,7 @@ static void P_DoNiGHTSCapsule(player_t *player) UINT8 em = P_GetNextEmerald(); // Only give it to ONE person, and THAT player has to get to the goal! mobj_t *emmo = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height, MT_GOTEMERALD); + emmo->health = em; // for identification P_SetTarget(&emmo->target, player->mo); P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em); P_SetTarget(&player->mo->tracer, emmo); @@ -6174,8 +6185,17 @@ static void P_DoNiGHTSCapsule(player_t *player) }*/ mobj_t *idya = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height, MT_GOTEMERALD); idya->extravalue2 = player->mare/5; + idya->health = player->mare + 1; // for identification P_SetTarget(&idya->target, player->mo); P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate + ((player->mare + 1) % 5)); + + if (player->mo->tracer) + { + P_SetTarget(&idya->hnext, player->mo->tracer); + idya->extravalue1 = (angle_t)(player->mo->tracer->extravalue1 - 72*ANG1); + if (idya->extravalue1 > player->mo->tracer->extravalue1) + idya->extravalue1 -= (72*ANG1)/idya->extravalue1; + } P_SetTarget(&player->mo->tracer, idya); } for (i = 0; i < MAXPLAYERS; i++) @@ -6204,6 +6224,51 @@ static void P_DoNiGHTSCapsule(player_t *player) player->capsule->lastlook = player->capsule->extravalue2 = -1; } +// +// P_MoveNiGHTSToDrone +// +// Pull NiGHTS to the drone during Nightserizing +// +static void P_MoveNiGHTSToDrone(player_t *player) +{ + if (!player->drone) + return; + + boolean flip = player->drone->flags2 & MF2_OBJECTFLIP; + boolean topaligned = (player->drone->flags & MF_SLIDEME) && !(player->drone->flags & MF_GRENADEBOUNCE); + boolean middlealigned = (player->drone->flags & MF_GRENADEBOUNCE) && !(player->drone->flags & MF_SLIDEME); + boolean bottomoffsetted = !(player->drone->flags & MF_SLIDEME) && !(player->drone->flags & MF_GRENADEBOUNCE); + fixed_t droneboxmandiff = max(player->drone->height - player->mo->height, 0); + fixed_t zofs; + + if (!flip) + { + if (topaligned) + zofs = droneboxmandiff; + else if (middlealigned) + zofs = droneboxmandiff / 2; + else if (bottomoffsetted) + zofs = FixedMul(24*FRACUNIT, player->drone->scale); + else + zofs = 0; + } + else + { + if (topaligned) + zofs = 0; + else if (middlealigned) + zofs = droneboxmandiff / 2; + else if (bottomoffsetted) + zofs = droneboxmandiff - FixedMul(24*FRACUNIT, player->drone->scale); + else + zofs = droneboxmandiff; + } + + player->mo->momx = player->mo->momy = player->mo->momz = 0; + P_TeleportMove(player->mo, player->drone->x, player->drone->y, player->drone->z + zofs); + P_SetTarget(&player->drone, NULL); +} + // // P_NiGHTSMovement // @@ -7126,6 +7191,13 @@ static void P_MovePlayer(player_t *player) return; } + // Suck player into their drone + if (player->drone) + { + P_MoveNiGHTSToDrone(player); + return; + } + // Test revamped NiGHTS movement. if (player->powers[pw_carry] == CR_NIGHTSMODE) {