diff --git a/src/d_player.h b/src/d_player.h index c133af703..eff8e54f5 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -234,7 +234,8 @@ typedef enum CR_ZOOMTUBE, CR_ROPEHANG, CR_MACESPIN, - CR_MINECART + CR_MINECART, + CR_PTERABYTE } carrytype_t; // pw_carry // Player powers. (don't edit this comment) diff --git a/src/dehacked.c b/src/dehacked.c index b2a0239c6..d19f43f9c 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8594,6 +8594,7 @@ struct { {"CR_ROPEHANG",CR_ROPEHANG}, {"CR_MACESPIN",CR_MACESPIN}, {"CR_MINECART",CR_MINECART}, + {"CR_PTERABYTE",CR_PTERABYTE}, // Ring weapons (ringweapons_t) // Useful for A_GiveWeapon diff --git a/src/p_enemy.c b/src/p_enemy.c index db76ab4f4..7ad65c1d0 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13878,6 +13878,7 @@ void A_SpawnPterabytes(mobj_t *actor) s = FINESINE(fa); waypoint = P_SpawnMobjFromMobj(actor, FixedMul(c, rad), FixedMul(s, rad), 0, MT_PTERABYTEWAYPOINT); waypoint->angle = ang + ANGLE_90; + P_SetTarget(&waypoint->tracer, actor); ptera = P_SpawnMobjFromMobj(waypoint, 0, 0, 0, MT_PTERABYTE); ptera->angle = waypoint->angle; P_SetTarget(&ptera->tracer, waypoint); diff --git a/src/p_inter.c b/src/p_inter.c index 0030e8e58..cbf8153b4 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -456,6 +456,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (P_PlayerCanDamage(player, special)) // Do you possess the ability to subdue the object? { + if (special->type == MT_PTERABYTE && special->target == player->mo && special->extravalue1 == 1) + return; // Can't hurt a Pterabyte if it's trying to pick you up + if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1)) { if (elementalpierce == 2) @@ -477,7 +480,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_TwinSpinRejuvenate(player, player->thokitem); } else + { + if (special->type == MT_PTERABYTE && special->target == player->mo) + return; // Don't hurt the player you're trying to grab + P_DamageMobj(toucher, special, special, 1, 0); + } return; } diff --git a/src/p_map.c b/src/p_map.c index cc9209ea8..1455f3a4f 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -490,6 +490,40 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) } } +static void P_DoPterabyteCarry(player_t *player, mobj_t *ptera) +{ + if (player->powers[pw_carry]) + return; + if (ptera->extravalue1 != 1) + return; // Not swooping + if (ptera->target != player->mo) + return; // Not swooping for you! + + if (player->spectator) + return; + + if ((player->mo->eflags & MFE_VERTICALFLIP) != (ptera->eflags & MFE_VERTICALFLIP)) + return; // Both should be in same gravity + + if (ptera->eflags & MFE_VERTICALFLIP) + { + if (ptera->ceilingz - (ptera->z + ptera->height) < player->mo->height - FixedMul(2*FRACUNIT, player->mo->scale)) + return; + } + else if (ptera->z - ptera->floorz < player->mo->height - FixedMul(2*FRACUNIT, player->mo->scale)) + return; // No room to pick up this guy! + + P_ResetPlayer(player); + P_SetTarget(&player->mo->tracer, ptera); + player->powers[pw_carry] = CR_PTERABYTE; + S_StartSound(player->mo, sfx_s3k4a); + P_UnsetThingPosition(player->mo); + player->mo->x = ptera->x; + player->mo->y = ptera->y; + P_SetThingPosition(player->mo); + ptera->movefactor = 3*TICRATE; +} + static void P_DoTailsCarry(player_t *sonic, player_t *tails) { INT32 p; @@ -920,6 +954,9 @@ static boolean PIT_CheckThing(mobj_t *thing) P_DamageMobj(thing, tmthing, tmthing, 1, 0); } + if (thing->type == MT_PTERABYTE && tmthing->player) + P_DoPterabyteCarry(tmthing->player, thing); + if (thing->type == MT_VULTURE && tmthing->type == MT_VULTURE) { fixed_t dx = thing->x - tmthing->x; diff --git a/src/p_mobj.c b/src/p_mobj.c index 4258ac402..4388c1bd6 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9078,7 +9078,18 @@ void P_MobjThinker(mobj_t *mobj) } case MT_PTERABYTE: { - if (mobj->extravalue1 == 0) // Hovering + if (mobj->extravalue1 & 4) // Cooldown after grabbing + { + if (mobj->movefactor) + mobj->movefactor--; + else + { + P_SetTarget(&mobj->target, NULL); + mobj->extravalue1 &= 3; + } + } + + if ((mobj->extravalue1 & 3) == 0) // Hovering { fixed_t vdist, hdist, time; fixed_t hspeed = 3*mobj->info->speed; @@ -9087,21 +9098,31 @@ void P_MobjThinker(mobj_t *mobj) var1 = 1; var2 = 0; A_CapeChase(mobj); + + if (mobj->target) + break; // Still carrying a player or in cooldown + P_LookForPlayers(mobj, true, false, 256*FRACUNIT); if (!mobj->target) break; - vdist = mobj->z - mobj->target->z; + vdist = mobj->z - mobj->target->z - mobj->target->height; if (vdist <= 0) + { + P_SetTarget(&mobj->target, NULL); break; + } hdist = R_PointToDist2(mobj->x, mobj->y, mobj->target->x, mobj->target->y); if (hdist > 450*FRACUNIT) + { + P_SetTarget(&mobj->target, NULL); break; + } P_SetMobjState(mobj, S_PTERABYTE_SWOOPDOWN); - mobj->extravalue1 = 1; + mobj->extravalue1++; S_StartSound(mobj, mobj->info->attacksound); time = FixedDiv(hdist, hspeed); mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y); @@ -9113,7 +9134,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->movecount = time >> FRACBITS; mobj->reactiontime = mobj->movecount; } - else if (mobj->extravalue1 == 1) // Swooping + else if ((mobj->extravalue1 & 3) == 1) // Swooping { mobj->reactiontime--; mobj->momz += mobj->extravalue2; @@ -9128,8 +9149,9 @@ void P_MobjThinker(mobj_t *mobj) else if (mobj->state - states == S_PTERABYTE_SWOOPUP) { P_SetMobjState(mobj, S_PTERABYTE_FLY1); - mobj->extravalue1 = 2; - P_SetTarget(&mobj->target, NULL); + mobj->extravalue1++; + if (mobj->target && mobj->target->tracer != mobj) + P_SetTarget(&mobj->target, NULL); // Failed to grab the target mobj->momx = mobj->momy = mobj->momz = 0; } } @@ -9140,7 +9162,7 @@ void P_MobjThinker(mobj_t *mobj) A_HomingChase(mobj); if (P_AproxDistance(mobj->x - mobj->tracer->x, mobj->y - mobj->tracer->y) <= mobj->info->speed) { - mobj->extravalue1 = 0; + mobj->extravalue1 -= 2; mobj->momx = mobj->momy = mobj->momz = 0; } } diff --git a/src/p_user.c b/src/p_user.c index 1ca955e49..4895095f2 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4279,6 +4279,9 @@ void P_DoJump(player_t *player, boolean soundandstate) if (player->mo->ceilingz-player->mo->floorz <= player->mo->height-1) return; + if (player->powers[pw_carry] == CR_PTERABYTE) + return; + // Jump this high. if (player->powers[pw_carry] == CR_PLAYER) { @@ -11518,6 +11521,36 @@ void P_PlayerThink(player_t *player) }*/ } +// Checks if the mobj is above lava. Used by Pterabyte. +static boolean P_MobjAboveLava(mobj_t *mobj) +{ + sector_t *sector = mobj->subsector->sector; + + if (sector->ffloors) + { + ffloor_t *rover; + + for (rover = sector->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE) || GETSECSPECIAL(rover->master->frontsector->special, 1) != 3) + continue; + + if (mobj->eflags & MFE_VERTICALFLIP) + { + if (*rover->bottomheight <= mobj->ceilingz && *rover->bottomheight >= mobj->z) + return true; + } + else + { + if (*rover->topheight >= mobj->floorz && *rover->topheight <= mobj->z) + return true; + } + } + } + + return false; +} + // // P_PlayerAfterThink // @@ -11831,6 +11864,60 @@ void P_PlayerAfterThink(player_t *player) } break; } + case CR_PTERABYTE: // being carried by a Pterabyte + { + mobj_t *ptera = player->mo->tracer; + mobj_t *spawnpoint = ptera->tracer->tracer; + player->mo->height = FixedDiv(P_GetPlayerHeight(player), FixedDiv(14 * FRACUNIT, 10 * FRACUNIT)); + + if (ptera->health <= 0) + goto dropoff; + + if (P_MobjAboveLava(ptera)) + goto dropoff; + + if (player->mo->eflags & MFE_VERTICALFLIP) + { + if ((ptera->z + ptera->height + player->mo->height + FixedMul(FRACUNIT, player->mo->scale)) <= ptera->ceilingz + && (ptera->eflags & MFE_VERTICALFLIP)) // Reverse gravity check for the carrier - Flame + player->mo->z = ptera->z + ptera->height + FixedMul(FRACUNIT, player->mo->scale); + + if (ptera->ceilingz - ptera->z > spawnpoint->ceilingz - spawnpoint->z + 512*FRACUNIT) + goto dropoff; + } + else + { + if ((ptera->z - player->mo->height - FixedMul(FRACUNIT, player->mo->scale)) >= ptera->floorz + && !(ptera->eflags & MFE_VERTICALFLIP)) // Correct gravity check for the carrier - Flame + player->mo->z = ptera->z - player->mo->height - FixedMul(FRACUNIT, player->mo->scale); + + if (ptera->z - ptera->floorz > spawnpoint->z - spawnpoint->floorz + 512 * FRACUNIT) + goto dropoff; + } + + ptera->movefactor--; + if (!ptera->movefactor) + goto dropoff; + + P_TryMove(player->mo, ptera->x, ptera->y, true); + player->mo->momx = ptera->momx; + player->mo->momy = ptera->momy; + player->mo->momz = ptera->momz; + + if (P_AproxDistance(player->mo->x - ptera->x, player->mo->y - ptera->y) > player->mo->radius) + goto dropoff; + + if (player->mo->state-states != S_PLAY_RIDE) + P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); + break; + + dropoff: + player->powers[pw_carry] = CR_NONE; + P_SetTarget(&player->mo->tracer, NULL); + ptera->movefactor = TICRATE; + ptera->extravalue1 |= 4; + break; + } default: break; }