mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-12-31 20:22:40 +00:00
- Gachabom hits an object and is destroyed immediately (does not bounce off like Orbinauts or Jawz). - Gachabom explosion spawns in its place. - (New) Gachabom physically returns to the player who threw it.
233 lines
4.7 KiB
C++
233 lines
4.7 KiB
C++
#include <algorithm>
|
|
|
|
#include "../d_player.h"
|
|
#include "../k_objects.h"
|
|
#include "../m_fixed.h"
|
|
#include "../info.h"
|
|
#include "../p_local.h"
|
|
#include "../r_main.h"
|
|
#include "../tables.h"
|
|
|
|
/* An object may not be visible on the same tic:
|
|
1) that it spawned
|
|
2) that it cycles to the next state */
|
|
#define BUFFER_TICS (2)
|
|
|
|
#define rebound_target(o) ((o)->target)
|
|
#define rebound_mode(o) ((o)->threshold)
|
|
#define rebound_timer(o) ((o)->reactiontime)
|
|
|
|
namespace
|
|
{
|
|
|
|
constexpr int kReboundSpeed = 128;
|
|
constexpr int kReboundAcceptPause = 17;
|
|
constexpr int kReboundAcceptDuration = 8;
|
|
constexpr int kOrbitRadius = 0;
|
|
|
|
enum class Mode : int
|
|
{
|
|
kAlpha = 0,
|
|
kBeta,
|
|
kOrbit,
|
|
};
|
|
|
|
fixed_t z_center(const mobj_t* mobj)
|
|
{
|
|
return mobj->z + (mobj->height / 2);
|
|
}
|
|
|
|
bool rebound_is_returning(const mobj_t* mobj)
|
|
{
|
|
return mobj->state == &states[S_GACHABOM_RETURNING];
|
|
}
|
|
|
|
fixed_t distance_to_target(const mobj_t* mobj)
|
|
{
|
|
const mobj_t* target = rebound_target(mobj);
|
|
const fixed_t zDelta = z_center(target) - mobj->z;
|
|
|
|
return FixedHypot(FixedHypot(target->x - mobj->x, target->y - mobj->y), zDelta);
|
|
}
|
|
|
|
bool award_target(mobj_t* mobj)
|
|
{
|
|
mobj_t* target = rebound_target(mobj);
|
|
player_t* player = target->player;
|
|
|
|
if (mobj->fuse)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (player == nullptr)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ((player->itemtype == KITEM_GACHABOM || player->itemtype == KITEM_NONE) && !player->itemRoulette.active)
|
|
{
|
|
rebound_timer(mobj)--;
|
|
|
|
if (rebound_timer(mobj) < 1)
|
|
{
|
|
player->itemtype = KITEM_GACHABOM;
|
|
player->itemamount++;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void chase_rebound_target(mobj_t* mobj)
|
|
{
|
|
const mobj_t* target = rebound_target(mobj);
|
|
const fixed_t zDelta = z_center(target) - mobj->z;
|
|
const fixed_t distance = distance_to_target(mobj);
|
|
const fixed_t travelDistance = kReboundSpeed * mapobjectscale;
|
|
|
|
if (distance <= travelDistance)
|
|
{
|
|
rebound_mode(mobj) = static_cast<int>(Mode::kOrbit);
|
|
|
|
// Freeze
|
|
mobj->momx = 0;
|
|
mobj->momy = 0;
|
|
mobj->momz = 0;
|
|
}
|
|
else
|
|
{
|
|
const angle_t facing = R_PointToAngle2(mobj->x, mobj->y, target->x, target->y);
|
|
|
|
P_InstaThrust(mobj, facing, travelDistance);
|
|
mobj->angle = facing;
|
|
|
|
// This has a nice effect of "jumping up" rather quickly
|
|
mobj->momz = zDelta / 4;
|
|
|
|
const tic_t t = distance_to_target(mobj) / travelDistance;
|
|
const fixed_t newSpeed = std::abs(mobj->scale - mobj->destscale) / std::max(t, 1u);
|
|
|
|
if (newSpeed > mobj->scalespeed)
|
|
{
|
|
mobj->scalespeed = newSpeed;
|
|
}
|
|
}
|
|
}
|
|
|
|
void orbit_target(mobj_t* mobj)
|
|
{
|
|
if (award_target(mobj))
|
|
{
|
|
mobj->fuse = kReboundAcceptDuration + BUFFER_TICS;
|
|
mobj->destscale = 0;
|
|
mobj->scalespeed = mobj->scale / kReboundAcceptDuration;
|
|
}
|
|
|
|
const mobj_t* target = rebound_target(mobj);
|
|
const fixed_t rad = (2 * (mobj->radius + target->radius)) + (kOrbitRadius * mapobjectscale);
|
|
|
|
P_MoveOrigin(mobj,
|
|
target->x - FixedMul(FCOS(mobj->angle), rad),
|
|
target->y - FixedMul(FSIN(mobj->angle), rad),
|
|
target->z + (target->height / 2));
|
|
|
|
constexpr angle_t kOrbitSpeed = ANGLE_MAX / (kReboundAcceptPause + kReboundAcceptDuration);
|
|
|
|
mobj->angle -= 2 * kOrbitSpeed;
|
|
}
|
|
|
|
// Copied from MT_BANANA_SPARK
|
|
void squish(mobj_t* mobj)
|
|
{
|
|
if (leveltime & 1)
|
|
{
|
|
mobj->spritexscale = mobj->spriteyscale = FRACUNIT;
|
|
}
|
|
else
|
|
{
|
|
if ((leveltime / 2) & 1)
|
|
{
|
|
mobj->spriteyscale = 3*FRACUNIT;
|
|
}
|
|
else
|
|
{
|
|
mobj->spritexscale = 3*FRACUNIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
void spawn_afterimages(mobj_t* mobj)
|
|
{
|
|
mobj_t* ghost = P_SpawnGhostMobj(mobj);
|
|
|
|
// Flickers every frame
|
|
ghost->extravalue1 = 1;
|
|
ghost->extravalue2 = 2;
|
|
|
|
// No transparency
|
|
ghost->renderflags = 0;
|
|
|
|
ghost->tics = 8;
|
|
}
|
|
|
|
}; // namespace
|
|
|
|
void Obj_GachaBomReboundThink(mobj_t* mobj)
|
|
{
|
|
if (P_MobjWasRemoved(rebound_target(mobj)))
|
|
{
|
|
P_RemoveMobj(mobj);
|
|
return;
|
|
}
|
|
|
|
if (static_cast<Mode>(rebound_mode(mobj)) == Mode::kOrbit)
|
|
{
|
|
// Ready to be delivered to the player
|
|
orbit_target(mobj);
|
|
squish(mobj);
|
|
}
|
|
else if (rebound_is_returning(mobj))
|
|
{
|
|
// Travelling back
|
|
chase_rebound_target(mobj);
|
|
squish(mobj);
|
|
spawn_afterimages(mobj);
|
|
}
|
|
|
|
// Now the gummy animation is over
|
|
if (mobj->sprite == SPR_GBOM)
|
|
{
|
|
if (static_cast<Mode>(rebound_mode(mobj)) == Mode::kBeta)
|
|
{
|
|
// Only the alpha object remains
|
|
P_RemoveMobj(mobj);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Obj_SpawnGachaBomRebound(mobj_t* source, mobj_t* target)
|
|
{
|
|
auto spawn = [&](angle_t angle, Mode mode)
|
|
{
|
|
mobj_t *x = P_SpawnMobjFromMobjUnscaled(source, 0, 0, target->height / 2, MT_GACHABOM_REBOUND);
|
|
|
|
x->color = target->color;
|
|
x->angle = angle;
|
|
|
|
P_InstaScale(x, 2 * x->scale);
|
|
|
|
rebound_mode(x) = static_cast<int>(mode);
|
|
rebound_timer(x) = kReboundAcceptPause;
|
|
|
|
P_SetTarget(&rebound_target(x), target);
|
|
};
|
|
|
|
spawn(0, Mode::kAlpha);
|
|
spawn(ANGLE_45, Mode::kBeta);
|
|
spawn(ANGLE_90, Mode::kBeta);
|
|
spawn(ANGLE_135, Mode::kBeta);
|
|
}
|