Merge branch 'hyudoro-alpha-beta-prerelease-early-access' into 'hyudoro-release-prep'

Hyudoro becomes very real

See merge request KartKrew/Kart!1504
This commit is contained in:
Oni 2023-09-18 07:26:52 +00:00
commit de84a5e879
16 changed files with 528 additions and 136 deletions

View file

@ -78,7 +78,8 @@ typedef enum
PF_KICKSTARTACCEL = 1<<4, // Accessibility feature: Is accelerate in kickstart mode?
PF_POINTME = 1<<5, // An object is calling for my attention (via Obj_PointPlayersToMobj). Unset every frame!
// 1<<6 free
PF_CASTSHADOW = 1<<6, // Something is casting a shadow on the player
PF_WANTSTOJOIN = 1<<7, // Spectator that wants to join

View file

@ -3792,6 +3792,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
// Caked-Up Booty-Sheet Ghost
"S_HYUDORO",
"S_HYUDORO_RETURNING",
// Grow
"S_GROW_PARTICLE",
@ -5935,7 +5936,7 @@ const char *const PLAYERFLAG_LIST[] = {
"KICKSTARTACCEL", // Accessibility feature: Is accelerate in kickstart mode?
"POINTME", // An object is calling for my attention (via Obj_PointPlayersToMobj). Unset every frame!
"\x01", // free: 1<<6 (name un-matchable)
"CASTSHADOW", // Something is casting a shadow on the player
"WANTSTOJOIN", // Spectator that wants to join

View file

@ -251,7 +251,7 @@ tic_t starttime = 3;
const tic_t bulbtime = TICRATE/2;
UINT8 numbulbs = 1;
INT32 hyudorotime = 7*TICRATE;
INT32 hyudorotime = 14*TICRATE;
INT32 stealtime = TICRATE/2;
INT32 sneakertime = TICRATE + (TICRATE/3);
INT32 itemtime = 8*TICRATE;

View file

@ -4508,6 +4508,7 @@ state_t states[NUMSTATES] =
{SPR_GTAR, FF_FULLBRIGHT|FF_PAPERSPRITE, -1, {NULL}, 5, 2, S_NULL}, // S_GARDENTOPARROW
{SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO
{SPR_HYUU, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO_RETURNING
{SPR_GRWP, FF_FULLBRIGHT|FF_ANIMATE, 13, {NULL}, 7, 1, S_NULL}, // S_GROW_PARTICLE
@ -24667,13 +24668,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
32*FRACUNIT, // radius
24*FRACUNIT, // height
40*FRACUNIT, // radius
80*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SPECIAL|MF_NOCLIP|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags
MF_SPECIAL|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_NOSQUISH, // flags
S_NULL // raisestate
},

View file

@ -4950,6 +4950,7 @@ typedef enum state
// Caked-Up Booty-Sheet Ghost
S_HYUDORO,
S_HYUDORO_RETURNING,
// Grow
S_GROW_PARTICLE,

View file

@ -218,7 +218,7 @@ static inline boolean PIT_SSMineChecks(mobj_t *thing)
if (!(thing->flags & MF_SHOOTABLE) || (thing->flags & MF_SCENERY))
return true;
if (thing->player && thing->player->spectator)
if (thing->player && (thing->player->spectator || thing->player->hyudorotimer > 0))
return true;
if (P_AproxDistance(P_AproxDistance(thing->x - grenade->x, thing->y - grenade->y), thing->z - grenade->z) > explodedist)

View file

@ -4323,7 +4323,11 @@ void K_ApplyTripWire(player_t *player, tripwirestate_t state)
}
player->tripwireState = state;
K_AddHitLag(player->mo, 10, false);
if (player->hyudorotimer <= 0)
{
K_AddHitLag(player->mo, 10, false);
}
if (state == TRIPSTATE_PASSED && player->spinouttimer &&
player->speed > 2 * K_GetKartSpeed(player, false, true))
@ -7734,6 +7738,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->cameraOffset = 0;
player->pflags &= ~(PF_CASTSHADOW);
if (player->curshield == KSHIELD_TOP)
{
mobj_t *top = K_GetGardenTop(player);
@ -11661,23 +11667,19 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if (player->hyudorotimer > 0)
{
if (leveltime & 1)
player->mo->renderflags |= RF_DONTDRAW | RF_MODULATE;
player->mo->renderflags &= ~K_GetPlayerDontDrawFlag(player);
if (!(leveltime & 1) && (player->hyudorotimer < (TICRATE/2) || player->hyudorotimer > hyudorotime-(TICRATE/2)))
{
player->mo->renderflags |= RF_DONTDRAW;
}
else
{
if (player->hyudorotimer >= (TICRATE/2) && player->hyudorotimer <= hyudorotime-(TICRATE/2))
player->mo->renderflags &= ~K_GetPlayerDontDrawFlag(player);
else
player->mo->renderflags &= ~RF_DONTDRAW;
player->mo->renderflags &= ~(RF_DONTDRAW | RF_BLENDMASK);
}
player->flashing = player->hyudorotimer; // We'll do this for now, let's people know about the invisible people through subtle hints
}
else if (player->hyudorotimer == 0)
{
player->mo->renderflags &= ~RF_DONTDRAW;
player->mo->renderflags &= ~RF_BLENDMASK;
}
if (player->trickpanel == 1)

View file

@ -17,6 +17,7 @@ void Obj_HyudoroDeploy(mobj_t *master);
void Obj_HyudoroThink(mobj_t *actor);
void Obj_HyudoroCenterThink(mobj_t *actor);
void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher);
boolean Obj_HyudoroShadowZ(mobj_t *actor, fixed_t *return_z, pslope_t **return_slope);
/* Garden Top */
void Obj_GardenTopDeploy(mobj_t *rider);
@ -218,6 +219,7 @@ void Obj_SetEmeraldAwardee(mobj_t *emerald, mobj_t *awardee);
/* Fake Shadow */
mobj_t *Obj_SpawnFakeShadow(mobj_t *from);
void Obj_FakeShadowThink(mobj_t *shadow);
boolean Obj_FakeShadowZ(const mobj_t *shadow, fixed_t *return_z, pslope_t **return_slope);
/* Checkpoints */
void Obj_ResetCheckpoints(void);

View file

@ -304,13 +304,8 @@ void Obj_BeginEmeraldOrbit(mobj_t *emerald, mobj_t *target, fixed_t radius, INT3
spawn_lens_flare(emerald);
}
void Obj_GiveEmerald(mobj_t *emerald)
static void give_player(mobj_t *emerald)
{
if (P_MobjWasRemoved(emerald_orbit(emerald)) || P_MobjWasRemoved(emerald_award(emerald)))
{
return;
}
player_t *player = emerald_award(emerald)->player;
if (!player)
@ -324,6 +319,37 @@ void Obj_GiveEmerald(mobj_t *emerald)
S_StartSound(emerald_award(emerald), emerald->info->deathsound);
}
void Obj_GiveEmerald(mobj_t *emerald)
{
if (P_MobjWasRemoved(emerald_orbit(emerald)) || P_MobjWasRemoved(emerald_award(emerald)))
{
return;
}
// FIXME: emerald orbiting behavior should become its own object. For now,
// though, enjoy these special conditions!
switch (emerald_award(emerald)->type)
{
case MT_PLAYER:
give_player(emerald);
break;
case MT_ITEMCAPSULE: // objects/hyudoro.c
// DMG_INSTAKILL to kill it without respawning later
P_KillMobj(emerald_award(emerald), emerald_orbit(emerald), emerald_orbit(emerald), DMG_INSTAKILL);
if (emerald_orbit(emerald)->player)
{
// Unlock item for stacked Hyudoros
emerald_orbit(emerald)->player->itemRoulette.reserved = 0;
}
break;
default:
break;
}
}
void Obj_SetEmeraldAwardee(mobj_t *emerald, mobj_t *awardee)
{
P_SetTarget(&emerald_award(emerald), awardee);

View file

@ -22,11 +22,13 @@
#include "../s_sound.h"
#include "../g_game.h"
#include "../k_hitlag.h"
#include "../p_slopes.h"
enum {
HYU_PATROL,
HYU_RETURN,
HYU_HOVER,
HYU_ORBIT,
};
// TODO: make these general functions
@ -37,34 +39,26 @@ K_GetSpeed (mobj_t *mobj)
return FixedHypot(mobj->momx, mobj->momy);
}
static void
K_ChangePlayerItem
( player_t * player,
INT32 itemtype,
INT32 itemamount)
{
player->itemtype = itemtype;
player->itemamount = itemamount;
K_UnsetItemOut(player);
}
#define hyudoro_mode(o) ((o)->extravalue1)
#define hyudoro_itemtype(o) ((o)->movefactor)
#define hyudoro_itemcount(o) ((o)->movecount)
#define hyudoro_hover_stack(o) ((o)->threshold)
#define hyudoro_next(o) ((o)->tracer)
#define hyudoro_stackpos(o) ((o)->reactiontime)
#define hyudoro_delivered(o) (hyudoro_itemtype(o) == KITEM_NONE)
// cannot be combined
#define hyudoro_center(o) ((o)->target)
#define hyudoro_target(o) ((o)->target)
#define hyudoro_stolefrom(o) ((o)->hnext)
#define hyudoro_capsule(o) ((o)->hprev)
#define hyudoro_timer(o) ((o)->movedir)
#define hyudoro_center_max_radius(o) ((o)->threshold)
#define hyudoro_center_master(o) ((o)->target)
#define HYU_VISUAL_HEIGHT (24)
static angle_t
trace_angle (mobj_t *hyu)
{
@ -90,7 +84,8 @@ get_look_angle (mobj_t *thing)
static boolean
is_hyudoro (mobj_t *thing)
{
return thing && thing->type == MT_HYUDORO;
return !P_MobjWasRemoved(thing) &&
thing->type == MT_HYUDORO;
}
static mobj_t *
@ -115,7 +110,7 @@ sine_bob
angle_t a,
fixed_t sineofs)
{
hyu->sprzoff = FixedMul(hyu->height,
hyu->sprzoff = FixedMul(HYU_VISUAL_HEIGHT * hyu->scale,
sineofs + FINESINE(a >> ANGLETOFINESHIFT));
}
@ -129,6 +124,13 @@ bob_in_place
(ANGLE_MAX / bob_speed), -(3*FRACUNIT/4));
}
static void
reset_shadow (mobj_t *hyu)
{
hyu->shadowcolor = 15;
hyu->whiteshadow = true;
}
static void
project_hyudoro (mobj_t *hyu)
{
@ -150,6 +152,36 @@ project_hyudoro (mobj_t *hyu)
hyu->angle = angle + ANGLE_90;
sine_bob(hyu, angle, FRACUNIT);
hyu->z = P_GetZAt(center->standingslope, hyu->x, hyu->y,
P_GetMobjGround(center));
if (P_IsObjectFlipped(hyu))
{
hyu->z -= hyu->height;
}
}
static void
rise_thru_stack (mobj_t *hyu)
{
mobj_t *target = hyudoro_target(hyu);
fixed_t spacer = ((target->height / 2) +
(HYU_VISUAL_HEIGHT * hyu->scale * 2));
fixed_t sink = hyudoro_stackpos(hyu) * spacer;
fixed_t zofs = abs(hyu->momz);
fixed_t d = (zofs - sink);
fixed_t speed = d / 8;
if (abs(d) < abs(speed))
zofs = sink;
else
zofs -= speed;
hyu->momz = zofs * P_MobjFlip(target);
}
static void
@ -161,19 +193,18 @@ project_hyudoro_hover (mobj_t *hyu)
angle_t ang = get_look_angle(target) + ANGLE_67h;
fixed_t rad = (target->radius * 2) + hyu->radius;
fixed_t zofs = hyudoro_stackpos(hyu) *
((target->height / 2) + (hyu->height * 2));
P_MoveOrigin(hyu,
target->x - P_ReturnThrustX(hyu, ang, rad),
target->y - P_ReturnThrustY(hyu, ang, rad),
target->z + (zofs * P_MobjFlip(target)));
target->z);
// Cancel momentum from HYU_RETURN.
// (And anything else! I don't trust this game!!)
hyu->momx = 0;
hyu->momy = 0;
rise_thru_stack(hyu);
hyu->angle = ang;
// copies sprite tilting
@ -183,6 +214,30 @@ project_hyudoro_hover (mobj_t *hyu)
bob_in_place(hyu, 64);
}
static boolean
project_hyudoro_orbit (mobj_t *hyu)
{
mobj_t *orbit = hyudoro_target(hyu);
if (P_MobjWasRemoved(orbit))
{
return false;
}
P_MoveOrigin(hyu, orbit->x, orbit->y, orbit->z);
hyu->destscale = orbit->scale;
mobj_t *facing = orbit->target;
if (!P_MobjWasRemoved(facing))
{
hyu->angle = R_PointToAngle2(
hyu->x, hyu->y, facing->x, facing->y);
}
return true;
}
static mobj_t *
find_duel_target (mobj_t *ignore)
{
@ -237,12 +292,6 @@ do_confused (mobj_t *hyu)
// Hyudoro is confused.
// Spin around, try to find a new target.
if (hyudoro_delivered(hyu))
{
// Already delivered, not confused
return;
}
// Try to find new target
P_SetTarget(&hyudoro_target(hyu),
find_duel_target(hyudoro_stolefrom(hyu)));
@ -253,7 +302,7 @@ do_confused (mobj_t *hyu)
// Bob very fast
bob_in_place(hyu, 32);
hyu->sprzoff += hyu->height;
hyu->sprzoff += HYU_VISUAL_HEIGHT * hyu->scale;
}
static void
@ -290,37 +339,38 @@ move_to_player (mobj_t *hyu)
hyu->z = target->z; // stay level with target
hyu->angle = angle;
hyu->color = target->color;
}
static void
deliver_item (mobj_t *hyu)
{
mobj_t *target = hyudoro_target(hyu);
player_t *player = target->player;
/* set physical position to visual position in stack */
hyu->z += hyu->momz;
hyu->momz = 0;
P_SetTarget(&hyudoro_target(hyu), NULL);
mobj_t *emerald = P_SpawnMobjFromMobj(
hyu, 0, 0, 0, MT_EMERALD);
if (player)
{
K_ChangePlayerItem(player,
hyudoro_itemtype(hyu),
player->itemamount + hyudoro_itemcount(hyu));
}
/* only want emerald for its orbiting behavior, so make
it invisible */
P_SetMobjState(emerald, S_INVISIBLE);
S_StartSound(target, sfx_itpick);
Obj_BeginEmeraldOrbit(
emerald, hyudoro_target(hyu), 0, 64, 128);
// Stop moving here
hyu->momx = 0;
hyu->momy = 0;
/* See Obj_GiveEmerald. I won't risk relying on the
Hyudoro object in case it is removed first. So go
through the capsule instead. */
Obj_SetEmeraldAwardee(emerald, hyudoro_capsule(hyu));
hyu->tics = 4;
/* hyudoro will teleport to emerald (orbit the player) */
hyudoro_mode(hyu) = HYU_ORBIT;
P_SetTarget(&hyudoro_target(hyu), emerald);
hyu->destscale = target->scale / 4;
hyu->scalespeed =
abs(hyu->scale - hyu->destscale) / hyu->tics;
// sets as already delivered
hyudoro_itemtype(hyu) = KITEM_NONE;
hyu->renderflags &= ~(RF_DONTDRAW | RF_BLENDMASK);
reset_shadow(hyu);
}
static void
@ -337,7 +387,17 @@ append_hyudoro
}
hyudoro_stackpos(hyu) = lastpos + 1;
*head = hyu;
P_SetTarget(head, hyu);
/* only first in list gets a shadow */
if (lastpos == 0)
{
reset_shadow(hyu);
}
else
{
hyu->shadowcolor = 31;/* black - hide it */
}
}
static void
@ -345,31 +405,106 @@ pop_hyudoro (mobj_t **head)
{
mobj_t *hyu = *head;
INT32 lastpos;
INT32 thispos;
if (is_hyudoro(hyu))
if (!is_hyudoro(hyu))
{
lastpos = hyudoro_stackpos(hyu);
hyu = hyudoro_next(hyu);
while (is_hyudoro(hyu))
{
thispos = hyudoro_stackpos(hyu);
hyudoro_stackpos(hyu) = lastpos;
lastpos = thispos;
hyu = hyudoro_next(hyu);
}
return;
}
*head = hyu;
INT32 lastpos = hyudoro_stackpos(hyu);
{
mobj_t *next = hyudoro_next(hyu);
P_SetTarget(head, next);
P_SetTarget(&hyudoro_next(hyu), NULL);
hyu = next;
}
if (!is_hyudoro(hyu))
{
return;
}
reset_shadow(hyu);/* show it */
do
{
INT32 thispos = hyudoro_stackpos(hyu);
hyudoro_stackpos(hyu) = lastpos;
lastpos = thispos;
hyu = hyudoro_next(hyu);
}
while (is_hyudoro(hyu));
}
static void hyudoro_set_held_item_from_player
static mobj_t *
spawn_capsule (mobj_t *hyu)
{
mobj_t *caps = P_SpawnMobjFromMobj(
hyu, 0, 0, 0, MT_ITEMCAPSULE);
/* hyudoro only needs its own shadow */
caps->shadowscale = 0;
caps->flags |=
MF_NOGRAVITY |
MF_NOCLIP |
MF_NOCLIPTHING |
MF_NOCLIPHEIGHT;
/* signal that this item capsule always puts items in the HUD */
caps->flags2 |= MF2_STRONGBOX;
P_SetTarget(&hyudoro_capsule(hyu), caps);
/* capsule teleports to hyudoro */
P_SetTarget(&caps->target, hyu);
/* so it looks like hyudoro is holding it */
caps->sprzoff = 20 * hyu->scale;
return caps;
}
static void
update_capsule_position (mobj_t *hyu)
{
mobj_t *caps = hyudoro_capsule(hyu);
if (P_MobjWasRemoved(caps))
return;
caps->extravalue1 = hyu->scale / 3;
/* hold it in the hyudoro's hands */
const fixed_t r = hyu->radius;
caps->sprxoff = FixedMul(r, FCOS(hyu->angle));
caps->spryoff = FixedMul(r, FSIN(hyu->angle));
}
static void
set_item
( mobj_t * hyu,
player_t *player)
INT32 item,
INT32 amount)
{
mobj_t *caps = P_MobjWasRemoved(hyudoro_capsule(hyu))
? spawn_capsule(hyu) : hyudoro_capsule(hyu);
hyudoro_itemtype(hyu) = item;
hyudoro_itemcount(hyu) = amount;
caps->threshold = hyudoro_itemtype(hyu);
caps->movecount = hyudoro_itemcount(hyu);
}
static void
hyudoro_set_held_item_from_player
( mobj_t * hyu,
player_t * player)
{
if (K_ItemEnabled(KITEM_KITCHENSINK))
{
@ -395,15 +530,13 @@ static void hyudoro_set_held_item_from_player
itemCooldowns[player->itemtype - 1] = 0;
}
hyudoro_itemtype(hyu) = KITEM_KITCHENSINK;
hyudoro_itemcount(hyu) = 1;
set_item(hyu, KITEM_KITCHENSINK, 1);
return;
}
}
hyudoro_itemtype(hyu) = player->itemtype;
hyudoro_itemcount(hyu) = player->itemamount;
set_item(hyu, player->itemtype, player->itemamount);
}
static boolean
@ -429,6 +562,8 @@ hyudoro_patrol_hit_player
if (player->hyudorotimer)
return false;
player->pflags |= PF_CASTSHADOW;
// NO ITEM?
if (!player->itemamount)
return false;
@ -439,9 +574,19 @@ hyudoro_patrol_hit_player
hyudoro_set_held_item_from_player(hyu, player);
if (!P_MobjWasRemoved(hyudoro_capsule(hyu)))
{
hyudoro_capsule(hyu)->extravalue2 = player->skincolor;
}
K_StripItems(player);
player->hyudorotimer = hyudorotime;
/* do not make 1st place invisible */
if (player->position != 1)
{
player->hyudorotimer = hyudorotime;
}
player->stealingtimer = hyudorotime;
P_SetTarget(&hyudoro_stolefrom(hyu), toucher);
@ -459,6 +604,14 @@ hyudoro_patrol_hit_player
hyu->renderflags &= ~(RF_DONTDRAW);
// Reset shadow to default (after alt_shadow)
reset_shadow(hyu);
// This will flicker the shadow
hyudoro_timer(hyu) = 18;
P_SetMobjState(hyu, S_HYUDORO_RETURNING);
return true;
}
@ -474,16 +627,13 @@ award_immediately (mobj_t *hyu)
return false;
}
if (player->itemamount &&
player->itemtype != hyudoro_itemtype(hyu))
{
if (!P_CanPickupItem(player, 1))
return false;
}
// Same as picking up paper items; get stacks
// immediately
if (!P_CanPickupItem(player, 3))
return false;
// Prevent receiving any more items or even stacked
// Hyudoros! Put on a timer so roulette cannot become
// locked permanently.
player->itemRoulette.reserved = 2*TICRATE;
}
deliver_item(hyu);
@ -530,6 +680,96 @@ hyudoro_hover_await_stack (mobj_t *hyu)
return true;
}
static void
trail_ghosts
( mobj_t * hyu,
boolean colorize)
{
// Spawns every other frame
if (leveltime & 1)
{
return;
}
mobj_t *ghost = P_SpawnGhostMobj(hyu);
// Flickers every frame
ghost->extravalue1 = 1;
ghost->extravalue2 = 2;
// copy per-splitscreen-player visibility
ghost->renderflags =
(hyu->renderflags & RF_DONTDRAW);
ghost->colorized = colorize;
ghost->tics = 8;
P_SetTarget(&ghost->tracer, hyu);
}
static void
trail_glow (mobj_t *hyu)
{
mobj_t *ghost = P_SpawnGhostMobj(hyu);
// Flickers every frame
ghost->extravalue1 = 1;
ghost->extravalue2 = 0;
ghost->renderflags = RF_ADD | RF_TRANS80;
ghost->tics = 2; // this actually does last one tic
ghost->momz = hyu->momz; // copy stack position
}
static void
blend_hover_hyudoro (mobj_t *hyu)
{
player_t *player = get_hyudoro_target_player(hyu);
hyu->renderflags &= ~(RF_BLENDMASK);
if (!player)
{
return;
}
/* 1st place: Hyudoro stack is unusable, so make a visual
indication */
if (player->position == 1)
{
hyu->renderflags |= RF_MODULATE;
trail_glow(hyu);
}
}
static void
alt_shadow (mobj_t *hyu)
{
/* spaced out pulse, fake randomness */
switch (leveltime % (7 + ((leveltime / 8) % 3)))
{
default:
hyu->shadowcolor = 15;
hyu->whiteshadow = false;
break;
case 1:
hyu->shadowcolor = 5;
hyu->whiteshadow = true;
break;
case 2:
hyu->shadowcolor = 181;
hyu->whiteshadow = true;
break;
case 3:
hyu->shadowcolor = 255;
hyu->whiteshadow = true;
break;
}
}
void
Obj_InitHyudoroCenter (mobj_t * center, mobj_t * master)
{
@ -570,51 +810,60 @@ Obj_HyudoroDeploy (mobj_t *master)
center->angle = master->angle;
Obj_InitHyudoroCenter(center, master);
// Update floorz to the correct position by indicating
// that it should be recalculated by P_MobjThinker.
center->floorz = master->z;
center->ceilingz = master->z + master->height;
center->z = P_GetMobjGround(center) - P_MobjFlip(center);
S_StartSound(master, sfx_s3k92); // scary ghost noise
}
void
Obj_HyudoroThink (mobj_t *hyu)
{
// Might get set from clipping slopes
hyu->momz = 0;
switch (hyudoro_mode(hyu))
{
case HYU_PATROL:
if (hyudoro_center(hyu))
project_hyudoro(hyu);
if (leveltime & 1)
{
mobj_t *ghost = P_SpawnGhostMobj(hyu);
// Flickers every frame
ghost->extravalue1 = 1;
ghost->extravalue2 = 2;
// copy per-splitscreen-player visibility
ghost->renderflags =
(hyu->renderflags & RF_DONTDRAW);
ghost->tics = 8;
P_SetTarget(&ghost->tracer, hyu);
}
trail_ghosts(hyu, false);
alt_shadow(hyu);
break;
case HYU_RETURN:
move_to_player(hyu);
trail_ghosts(hyu, true);
if (hyudoro_timer(hyu) > 0)
hyu->whiteshadow = !hyu->whiteshadow;
break;
case HYU_HOVER:
if (hyudoro_target(hyu))
{
project_hyudoro_hover(hyu);
hyudoro_hover_await_stack(hyu);
if (hyudoro_hover_await_stack(hyu))
break;
}
blend_hover_hyudoro(hyu);
break;
case HYU_ORBIT:
if (!project_hyudoro_orbit(hyu))
{
P_RemoveMobj(hyu);
return;
}
break;
}
update_capsule_position(hyu);
if (hyudoro_timer(hyu) > 0)
hyudoro_timer(hyu)--;
}
void
@ -642,3 +891,21 @@ Obj_HyudoroCollide
break;
}
}
boolean
Obj_HyudoroShadowZ
( mobj_t * hyu,
fixed_t * return_z,
pslope_t ** return_slope)
{
if (hyudoro_mode(hyu) != HYU_PATROL)
return false;
if (P_MobjWasRemoved(hyudoro_center(hyu)))
return false;
*return_z = hyu->z;
*return_slope = hyudoro_center(hyu)->standingslope;
return true;
}

View file

@ -1,3 +1,6 @@
#include <optional>
#include <utility>
#include "../info.h"
#include "../k_objects.h"
#include "../p_local.h"
@ -19,6 +22,28 @@ struct Shadow : mobj_t
bool valid() const { return !P_MobjWasRemoved(this) && !P_MobjWasRemoved(follow()); }
std::optional<std::pair<fixed_t, pslope_t*>> z_position() const
{
switch (follow()->type)
{
case MT_HYUDORO: {
fixed_t z;
pslope_t* slope;
if (Obj_HyudoroShadowZ(follow(), &z, &slope))
{
return {{z, slope}};
}
break;
}
default:
break;
}
return {};
}
void destroy() { P_RemoveMobj(this); }
void move()
@ -58,3 +83,27 @@ void Obj_FakeShadowThink(mobj_t* shadow)
x->move();
}
boolean Obj_FakeShadowZ(const mobj_t* shadow, fixed_t* return_z, pslope_t** return_slope)
{
auto x = static_cast<const Shadow*>(shadow);
if (!x->valid())
{
return false;
}
auto pair = x->z_position();
if (!pair)
{
return false;
}
auto [z, slope] = *pair;
*return_z = z;
*return_slope = slope;
return true;
}

View file

@ -1687,7 +1687,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
INT16 spacing = (target->radius >> 1) / target->scale;
// set respawn fuse
if (K_CapsuleTimeAttackRules() == true) // no respawns
if (damagetype == DMG_INSTAKILL || K_CapsuleTimeAttackRules() == true) // no respawns
;
else if (target->threshold == KITEM_SUPERRING)
target->fuse = 20*TICRATE;
@ -1752,18 +1752,22 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
{
player_t *player = source->player;
// special behavior for ring capsules
if (target->threshold == KITEM_SUPERRING)
// MF2_STRONGBOX: always put the item right in the hotbar!
if (!(target->flags2 & MF2_STRONGBOX))
{
K_AwardPlayerRings(player, 5 * target->movecount, true);
break;
}
// special behavior for ring capsules
if (target->threshold == KITEM_SUPERRING)
{
K_AwardPlayerRings(player, 5 * target->movecount, true);
break;
}
// special behavior for SPB capsules
if (target->threshold == KITEM_SPB)
{
K_ThrowKartItem(player, true, MT_SPB, 1, 0, 0);
break;
// special behavior for SPB capsules
if (target->threshold == KITEM_SPB)
{
K_ThrowKartItem(player, true, MT_SPB, 1, 0, 0);
break;
}
}
if (target->threshold < 1 || target->threshold >= NUMKARTITEMS) // bruh moment prevention
@ -2301,7 +2305,7 @@ static void AddNullHitlag(player_t *player, tic_t oldHitlag)
// 1) repeating damage doesn't count
// 2) new damage sources still count
if (player->timeshit <= player->timeshitprev)
if (player->timeshit <= player->timeshitprev || player->hyudorotimer > 0)
{
player->nullHitlag += (player->mo->hitlag - oldHitlag);
}

View file

@ -4443,7 +4443,9 @@ static void P_RefreshItemCapsuleParts(mobj_t *mobj)
}
// update cap colors
if (itemType == KITEM_SUPERRING)
if (mobj->extravalue2)
color = mobj->extravalue2;
else if (itemType == KITEM_SUPERRING)
{
color = SKINCOLOR_GOLD;
newRenderFlags |= RF_SEMIBRIGHT;
@ -4487,7 +4489,10 @@ static void P_RefreshItemCapsuleParts(mobj_t *mobj)
count = mobj->movecount;
break;
case KITEM_SUPERRING: // always display the number, and multiply it by 5
count = mobj->movecount * 5;
if (mobj->flags2 & MF2_STRONGBOX)
count = mobj->movecount * 20; // give Super Rings
else
count = mobj->movecount * 5;
break;
case KITEM_SAD: // never display the number
case KITEM_SPB:

View file

@ -10,6 +10,7 @@
#include "d_player.h"
#include "info.h"
#include "k_objects.h"
#include "p_tick.h"
#include "r_splats.h"
#include "r_things.h"
@ -27,6 +28,11 @@ INT32 R_ThingLightLevel(mobj_t* thing)
// Darken on every other frame of instawhip cooldown
lightlevel -= 128;
}
if (player->pflags & PF_CASTSHADOW)
{
lightlevel -= 255;
}
}
return lightlevel;
@ -59,3 +65,21 @@ boolean R_SplatSlope(mobj_t* mobj, vector3_t position, pslope_t* slope)
return false;
}
boolean R_CustomShadowZ(mobj_t* thing, fixed_t *z, pslope_t** slope)
{
switch (thing->type)
{
case MT_SHADOW:
if (Obj_FakeShadowZ(thing, z, slope))
{
return true;
}
break;
default:
break;
}
return false;
}

View file

@ -1315,6 +1315,14 @@ fixed_t R_GetShadowZ(
sector_t *sector;
ffloor_t *rover;
if (R_CustomShadowZ(thing, &groundz, &groundslope))
{
if (shadowslope != NULL)
*shadowslope = groundslope;
return groundz;
}
// for frame interpolation
interpmobjstate_t interp = {0};

View file

@ -95,6 +95,7 @@ boolean R_ThingIsFlashing(mobj_t *thing);
INT32 R_ThingLightLevel(mobj_t *thing);
boolean R_SplatSlope(mobj_t *thing, vector3_t position, pslope_t *slope);
boolean R_CustomShadowZ(mobj_t *thing, fixed_t *return_z, pslope_t **return_slope);
// --------------
// MASKED DRAWING