Merge branch 'pull-up' into 'master'

Hardcode SA2 Pulley (ipullup.lua)

See merge request KartKrew/Kart!2220
This commit is contained in:
Oni 2024-04-04 02:01:32 +00:00
commit 2a2a03eed9
10 changed files with 380 additions and 0 deletions

View file

@ -804,6 +804,8 @@ struct player_t
UINT8 dashRingPullTics; // Timer during which the player is pulled towards a dash ring
UINT8 dashRingPushTics; // Timer during which the player displays effects and has no gravity after being thrust by a dash ring
boolean pullup; // True if the player is attached to a pullup hook
tic_t ebrakefor; // Ebrake timer, used for visuals.
UINT16 faultflash; // Used for misc FAULT visuals

View file

@ -3933,6 +3933,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_CABOTRON",
"MT_CABOTRONSTAR",
"MT_STARSTREAM",
"MT_IPULLUP",
"MT_PULLUPHOOK",
};
const char *const MOBJFLAG_LIST[] = {

View file

@ -762,6 +762,10 @@ char sprnames[NUMSPRITES + 1][5] =
"DIEM", // smoke
"DIEN", // explosion
// Pulley
"HCCH",
"HCHK",
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
"VIEW",
};
@ -22014,6 +22018,58 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MF_SCENERY|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
{ // MT_IPULLUP
3444, // doomednum
S_INVISIBLE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
32*FRACUNIT, // radius
32*FRACUNIT, // height
0, // dispoffset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_PULLUPHOOK
3444, // doomednum
S_INVISIBLE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
64*FRACUNIT, // radius
64*FRACUNIT, // height
0, // dispoffset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOCLIPHEIGHT|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP, // flags
S_NULL // raisestate
},
};

View file

@ -1297,6 +1297,10 @@ typedef enum sprite
SPR_DIEM, // smoke
SPR_DIEN, // explosion
// Pulley
SPR_HCCH,
SPR_HCHK,
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
SPR_VIEW,
@ -4980,6 +4984,9 @@ typedef enum mobj_type
MT_CABOTRONSTAR,
MT_STARSTREAM,
MT_IPULLUP,
MT_PULLUPHOOK,
MT_FIRSTFREESLOT,
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
NUMMOBJTYPES

View file

@ -425,6 +425,10 @@ boolean Obj_DestroyKart(mobj_t *kart);
void Obj_DestroyedKartParticleThink(mobj_t *part);
void Obj_DestroyedKartParticleLanding(mobj_t *part);
/* Pulley */
void Obj_PulleyThink(mobj_t *root);
void Obj_PulleyHookTouch(mobj_t *special, mobj_t *toucher);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -57,6 +57,7 @@ target_sources(SRB2SDL2 PRIVATE
powerup-spinner.cpp
adventure-air-booster.c
destroyed-kart.cpp
pulley.cpp
)
add_subdirectory(versus)

292
src/objects/pulley.cpp Normal file
View file

@ -0,0 +1,292 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2024 by "Lat'"
// Copyright (C) 2024 by Kart Krew
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
#include <cmath>
#include "objects.hpp"
#include "../d_player.h"
using namespace srb2::objects;
namespace
{
struct Pulley;
struct Hook : Mobj
{
void target() = delete;
Mobj* player() const { return Mobj::target(); }
void player(Mobj* n) { Mobj::target(n); }
void tracer() = delete;
Pulley* pulley() const;
void pulley(Pulley* n);
void touch(Mobj* toucher);
};
struct Pulley : Mobj
{
// hook states to keep the code clean :)
enum class Mode : INT32
{
kNull = 0, // not set
kIdle = 1, // wait for player
kHook = 2, // player hooked, short delay before we start pulling
kPull = 3, // pulling the player upwards
kDown = 4, // player has been flung, go back down
};
static constexpr tic_t kPullupDelay = TICRATE/4;
static Fixed max_pullup_speed() { return 32*mapobjectscale; }
// how far down do we extend the hook from our current
// position?
void cvmem() = delete;
Fixed height() const { return mobj_t::cvmem; }
void height(Fixed n) { mobj_t::cvmem = n; }
// just makes it easier
Fixed bottom() const { return z - height(); }
void target() = delete;
Hook* hook() const { return Mobj::target<Hook>(); }
void hook(Hook* n) { Mobj::target(n); }
void extravalue1() = delete;
Mode mode() const { return static_cast<Mode>(mobj_t::extravalue1); }
void mode(Mode n) { mobj_t::extravalue1 = static_cast<INT32>(n); }
void thing_args() = delete;
bool trick_bit() const { return mobj_t::thing_args[0] & 1; }
void extravalue2() = delete;
tic_t ticker() const { return mobj_t::extravalue2; }
void ticker(tic_t n) { mobj_t::extravalue2 = n; }
void tracer() = delete;
Mobj* rope() const { return Mobj::tracer(); }
void rope(Mobj* n) { Mobj::tracer(n); }
void init()
{
if (Mobj::valid(hook()))
hook()->remove();
if (Mobj::valid(rope()))
rope()->remove();
if (!spawnpoint)
return; // what the fuck
height(spawnpoint->angle * FRACUNIT);
// spawn the hook:
if (Hook* h = spawn<Hook>({x, y, bottom()}, MT_PULLUPHOOK))
{
h->sprite = SPR_HCHK;
h->frame = 0;
h->color = SKINCOLOR_RED;
hook(h); // don't lose track of that.
h->pulley(this); // point to daddy
// set idle state
mode(Mode::kIdle);
}
if (Mobj* h = spawn<Mobj>({x, y, bottom()}, MT_THOK))
{
// jartha note: this visual has been completely replaced vs the old lua version
h->sprite = SPR_HCCH;
h->frame = 0;
h->tics = -1;
rope(h);
animate();
h->mobj_t::old_spriteyscale = h->mobj_t::spriteyscale;
}
}
bool think()
{
if (mode() == Mode::kNull)
{
init();
return true;
}
if (!Mobj::valid(hook()))
{
mode(Mode::kNull); // wtf! force respawn hook
return false;
}
// handle functionality:
auto mode_handler = [&]
{
switch (mode())
{
case Mode::kHook:
return think_hook();
case Mode::kPull:
return think_pull();
case Mode::kDown:
return think_down();
default:
return true;
}
};
if (!mode_handler())
return false;
// handle the hook visuals here
animate();
return true;
}
private:
bool think_player()
{
// Hook the player and ensure they remain in place!
Mobj* pmo = hook()->player();
if (!Mobj::valid(pmo) || !pmo->player)
{
mode(Mode::kNull); // reset hook
return false;
}
pmo->flags |= MF_NOGRAVITY;
pmo->move_origin(hook());
pmo->angle = angle;
return true;
};
bool think_hook()
{
if (!think_player())
return false;
// wait .5 second before pulling
ticker(ticker() + 1);
if (ticker() > kPullupDelay)
{
mode(Mode::kPull);
ticker(0); // (don't forget to reset that...)
hook()->momz = flip(mapobjectscale/4);
}
return true;
}
bool think_pull()
{
if (!think_player())
return false;
hook()->momz = hook()->momz * 14 / 10;
if (std::abs(hook()->momz) > max_pullup_speed())
hook()->momz = flip(max_pullup_speed());
// reaching the top
if (hook()->z > z)
apex();
return true;
}
void apex()
{
mode(Mode::kDown);
Mobj* pmo = hook()->player();
P_ResetPlayer(pmo->player);
// special flag sets trick panel state
if (trick_bit()) // tyron 2023-10-30 spooky no look UDMF fix
{
K_DoPogoSpring(pmo, 32*FRACUNIT, 0);
pmo->player->trickpanel = TRICKSTATE_READY;
// jartha note: trickdelay does not exist, maybe it got replaced at some point?
//pmo->player->trickdelay = 8;
}
pmo->momz = hook()->momz;
pmo->player->pullup = false;
pmo->flags &= ~MF_NOGRAVITY;
hook()->momz = 0; // stop!
hook()->player(nullptr); // this looks stupid, but anyway this makes the hook forget about the player
}
bool think_down()
{
// go back down slowly.
hook()->momz = -24 * mapobjectscale;
if (hook()->z < bottom())
{
// jartha note: lua discrepancy: setting z in lua does P_CheckPosition. Is it fine to skip that?
hook()->z = bottom();
hook()->momz = 0;
mode(Mode::kIdle); // aaand we're ready again.
}
return true;
}
void animate()
{
if (!Mobj::valid(rope()) || !Mobj::valid(hook()))
return;
rope()->z = hook()->top();
rope()->spriteyscale(Fixed {std::max(0, z - hook()->top())} / std::max<Fixed>(1, 32 * rope()->scale()));
}
};
Pulley* Hook::pulley() const
{
return Mobj::tracer<Pulley>();
}
void Hook::pulley(Pulley* n)
{
Mobj::tracer(n);
}
void Hook::touch(Mobj* toucher)
{
if (Mobj::valid(player()))
return; // nope
if (!Mobj::valid(pulley()))
return; // wtf
if (pulley()->mode() != Pulley::Mode::kIdle)
return; // hook is busy
if (toucher->player->pullup)
return; // Already hooked!
player(toucher);
pulley()->mode(Pulley::Mode::kHook);
P_ResetPlayer(toucher->player); // stop everything we're doing
toucher->player->pullup = true;
pulley()->angle = toucher->angle;
}
}; // namespace
void Obj_PulleyThink(mobj_t *root)
{
static_cast<Pulley*>(root)->think();
}
void Obj_PulleyHookTouch(mobj_t *special, mobj_t *toucher)
{
static_cast<Hook*>(special)->touch(static_cast<Mobj*>(toucher));
}

View file

@ -1058,6 +1058,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
Obj_SSBumperTouchSpecial(special, toucher);
return;
case MT_PULLUPHOOK:
Obj_PulleyHookTouch(special, toucher);
return;
default: // SOC or script pickup
P_SetTarget(&special->target, toucher);
break;

View file

@ -6566,6 +6566,11 @@ static void P_MobjSceneryThink(mobj_t *mobj)
Obj_TickPowerUpSpinner(mobj);
return;
}
case MT_IPULLUP:
{
Obj_PulleyThink(mobj);
return;
}
default:
if (mobj->fuse)
{ // Scenery object fuse! Very basic!
@ -10604,6 +10609,8 @@ fixed_t P_GetMobjDefaultScale(mobj_t *mobj)
return 4*FRACUNIT;
case MT_SCRIPT_THING_ORB:
return 2*FRACUNIT;
case MT_PULLUPHOOK:
return 2*FRACUNIT;
default:
break;
}

View file

@ -540,6 +540,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].dashRingPullTics);
WRITEUINT8(save->p, players[i].dashRingPushTics);
WRITEUINT8(save->p, players[i].pullup);
WRITEUINT32(save->p, players[i].ebrakefor);
WRITEUINT32(save->p, players[i].roundscore);
@ -1130,6 +1132,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].dashRingPullTics = READUINT8(save->p);
players[i].dashRingPushTics = READUINT8(save->p);
players[i].pullup = READUINT8(save->p);
players[i].ebrakefor = READUINT32(save->p);
players[i].roundscore = READUINT32(save->p);