mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Gachabom rebound
- 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.
This commit is contained in:
parent
74e3ea89d6
commit
696e9e09c9
6 changed files with 260 additions and 3 deletions
|
|
@ -24583,7 +24583,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_GACHABOM, // deathstate
|
||||
S_GACHABOM_DEAD, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_s3k5d, // deathsound
|
||||
28*FRACUNIT, // speed
|
||||
|
|
|
|||
|
|
@ -133,6 +133,10 @@ boolean Obj_RandomItemSpawnIn(mobj_t *mobj);
|
|||
fixed_t Obj_RandomItemScale(fixed_t oldScale);
|
||||
void Obj_RandomItemSpawn(mobj_t *mobj);
|
||||
|
||||
/* Gachabom Rebound */
|
||||
void Obj_GachaBomReboundThink(mobj_t *mobj);
|
||||
void Obj_SpawnGachaBomRebound(mobj_t *source, mobj_t *target);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -19,4 +19,5 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
random-item.c
|
||||
instawhip.c
|
||||
block.c
|
||||
gachabom-rebound.cpp
|
||||
)
|
||||
|
|
|
|||
233
src/objects/gachabom-rebound.cpp
Normal file
233
src/objects/gachabom-rebound.cpp
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
#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);
|
||||
}
|
||||
|
|
@ -277,9 +277,20 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
|
|||
S_StartSound(t1, t1->info->deathsound);
|
||||
P_KillMobj(t1, t2, t2, DMG_NORMAL);
|
||||
|
||||
P_SetObjectMomZ(t1, 24*FRACUNIT, false);
|
||||
if (t1->type == MT_GACHABOM)
|
||||
{
|
||||
// Instead of flying out at an angle when
|
||||
// destroyed, spawn an explosion and eventually
|
||||
// return to sender. The original Gachabom will be
|
||||
// removed next tic (see deathstate).
|
||||
Obj_SpawnGachaBomRebound(t1, orbinaut_owner(t1));
|
||||
}
|
||||
else
|
||||
{
|
||||
P_SetObjectMomZ(t1, 24*FRACUNIT, false);
|
||||
|
||||
P_InstaThrust(t1, bounceangle, 16*FRACUNIT);
|
||||
P_InstaThrust(t1, bounceangle, 16*FRACUNIT);
|
||||
}
|
||||
}
|
||||
|
||||
if (sprung)
|
||||
|
|
|
|||
|
|
@ -9522,6 +9522,14 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case MT_GACHABOM_REBOUND:
|
||||
Obj_GachaBomReboundThink(mobj);
|
||||
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// check mobj against possible water content, before movement code
|
||||
P_MobjCheckWater(mobj);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue