diff --git a/src/cvars.cpp b/src/cvars.cpp index 4fd79c158..2dad5faef 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -773,6 +773,7 @@ extern CV_PossibleValue_t capsuletest_cons_t[]; void CapsuleTest_OnChange(void); consvar_t cv_capsuletest = OnlineCheat("capsuletest", "Off").values(capsuletest_cons_t).onchange(CapsuleTest_OnChange).description("Force item capsule spawning rules"); +consvar_t cv_debugcheese = OnlineCheat("debugcheese", "Off").on_off().description("Disable checks that prevent farming item boxes"); consvar_t cv_debugencorevote = OnlineCheat("debugencorevote", "Off").on_off().description("Force encore choice to appear on vote screen"); void ForceSkin_OnChange(void); diff --git a/src/d_player.h b/src/d_player.h index 1bc0d07e6..03efa55ec 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -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 @@ -442,6 +443,7 @@ struct itemroulette_t boolean eggman; boolean ringbox; boolean autoroulette; + UINT8 reserved; }; // enum for bot item priorities diff --git a/src/deh_tables.c b/src/deh_tables.c index f4112f614..b36fa412c 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -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", @@ -5923,8 +5924,8 @@ const char *const MAPTHINGFLAG_LIST[4] = { const char *const PLAYERFLAG_LIST[] = { "GODMODE", - // free: 1<<1 and 1<<2 (name un-matchable) - "\x01", + "\x01", // free: 1<<1 (name un-matchable) + "AUTOROULETTE", // Item box accessibility // Look back VFX has been spawned @@ -5932,9 +5933,10 @@ const char *const PLAYERFLAG_LIST[] = { "GAINAX", // Accessibility and cheats - "KICKSTARTACCEL", // Is accelerate in kickstart mode? - "GODMODE", - "NOCLIP", + "KICKSTARTACCEL", // Accessibility feature: Is accelerate in kickstart mode? + "POINTME", // An object is calling for my attention (via Obj_PointPlayersToMobj). Unset every frame! + + "CASTSHADOW", // Something is casting a shadow on the player "WANTSTOJOIN", // Spectator that wants to join @@ -5966,8 +5968,11 @@ const char *const PLAYERFLAG_LIST[] = { "HITFINISHLINE", // Already hit the finish line this tic "WRONGWAY", // Moving the wrong way with respect to waypoints? - "SHRINKME", - "SHRINKACTIVE", + "SHRINKME", // "Shrink me" cheat preference + "SHRINKACTIVE", // "Shrink me" cheat is in effect. (Can't be disabled mid-race) + + "VOID", // Removed from reality! When leaving hitlag, reenable visibility+collision and kill speed. + "NOFASTFALL", // Has already done ebrake/fastfall behavior for this input. Fastfalling needs a new input to prevent unwanted bounces on unexpected airtime. NULL // stop loop here. }; diff --git a/src/g_game.c b/src/g_game.c index 4769c8b8b..a52d523c1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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; diff --git a/src/info.c b/src/info.c index 2343b81e3..b1f696e79 100644 --- a/src/info.c +++ b/src/info.c @@ -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 }, diff --git a/src/info.h b/src/info.h index ef47c05bc..dc5257a08 100644 --- a/src/info.h +++ b/src/info.h @@ -4950,6 +4950,7 @@ typedef enum state // Caked-Up Booty-Sheet Ghost S_HYUDORO, + S_HYUDORO_RETURNING, // Grow S_GROW_PARTICLE, diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 74bc74e71..b897873a8 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -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) diff --git a/src/k_hud.c b/src/k_hud.c index dda443852..42773bb77 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1413,6 +1413,10 @@ static void K_drawKartItem(void) else localpatch[1] = kp_nodraw; } + else if (stplyr->itemRoulette.reserved > 0) + { + localpatch[1] = kp_nodraw; + } else { if (stplyr->itemamount <= 0) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index d6c108dc6..c2e4cf564 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -448,7 +448,8 @@ bool is_object_tracking_target(const mobj_t* mobj) return inDuel == false && battleovertime.enabled; case MT_EMERALD: - return (specialstageinfo.valid && specialstageinfo.ufo) || is_player_tracking_target(); + return Obj_EmeraldCanHUDTrack(mobj) && + ((specialstageinfo.valid && specialstageinfo.ufo) || is_player_tracking_target()); case MT_MONITOR: return is_player_tracking_target() && Obj_MonitorGetEmerald(mobj) != 0; diff --git a/src/k_kart.c b/src/k_kart.c index e4459385b..00ef4bd21 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -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) diff --git a/src/k_objects.h b/src/k_objects.h index 7050ddff4..d5e06e8e8 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -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); @@ -213,6 +214,13 @@ void Obj_EmeraldThink(mobj_t *emerald); void Obj_EmeraldFlareThink(mobj_t *flare); void Obj_BeginEmeraldOrbit(mobj_t *emerald, mobj_t *target, fixed_t radius, INT32 revolution_time, tic_t fuse); void Obj_GiveEmerald(mobj_t *emerald); +void Obj_SetEmeraldAwardee(mobj_t *emerald, mobj_t *awardee); +boolean Obj_EmeraldCanHUDTrack(const mobj_t *emerald); + +/* 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); diff --git a/src/k_roulette.c b/src/k_roulette.c index 3dce1b705..8868286e5 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -1509,6 +1509,7 @@ void K_StopRoulette(itemroulette_t *const roulette) roulette->active = false; roulette->eggman = false; roulette->ringbox = false; + roulette->reserved = 0; } /*-------------------------------------------------- @@ -1574,6 +1575,12 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) itemroulette_t *const roulette = &player->itemRoulette; boolean confirmItem = false; + if (roulette->reserved > 0) + { + roulette->reserved--; + return; + } + // This makes the roulette cycle through items. // If this isn't active, you shouldn't be here. if (roulette->active == false) diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index c3b01f4a0..89d5a17c1 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -29,4 +29,5 @@ target_sources(SRB2SDL2 PRIVATE sneaker-panel.c emerald.c checkpoint.cpp + shadow.cpp ) diff --git a/src/objects/emerald.c b/src/objects/emerald.c index fdf7449f2..65b23edb6 100644 --- a/src/objects/emerald.c +++ b/src/objects/emerald.c @@ -15,6 +15,8 @@ #define emerald_target_radius(o) ((o)->extravalue2) #define emerald_z_shift(o) ((o)->reactiontime) #define emerald_scale_rate(o) ((o)->movefactor) +#define emerald_orbit(o) ((o)->target) +#define emerald_award(o) ((o)->tracer) // Think of this like EMERALD_SPEED_UP / EMERALD_SPEED_UP_RATE #define EMERALD_SPEED_UP (1) // speed up by this much... @@ -84,7 +86,7 @@ static fixed_t get_target_z(mobj_t *emerald) { fixed_t shift = FixedMul(emerald_z_shift(emerald), FRACUNIT - get_suck_factor(emerald)); - return center_of(emerald->target) + get_bob(emerald) + shift; + return center_of(emerald_orbit(emerald)) + get_bob(emerald) + shift; } static void speed_up(mobj_t *emerald) @@ -116,8 +118,8 @@ static void Obj_EmeraldOrbitPlayer(mobj_t *emerald) P_MoveOrigin( emerald, - emerald->target->x + x, - emerald->target->y + y, + emerald_orbit(emerald)->x + x, + emerald_orbit(emerald)->y + y, get_target_z(emerald) ); @@ -133,9 +135,9 @@ static void Obj_EmeraldOrbitPlayer(mobj_t *emerald) void Obj_EmeraldThink(mobj_t *emerald) { - if (!P_MobjWasRemoved(emerald->target)) + if (!P_MobjWasRemoved(emerald_orbit(emerald))) { - switch (emerald->target->type) + switch (emerald_orbit(emerald)->type) { case MT_SPECIAL_UFO: Obj_UFOEmeraldThink(emerald); @@ -256,7 +258,7 @@ static void spawn_lens_flare(mobj_t *emerald) mobj_t *flare = P_SpawnMobjFromMobj(emerald, 0, 0, 0, MT_EMERALDFLARE); P_SetTarget(&flare->target, emerald); - P_InstaScale(flare, emerald->target->scale); + P_InstaScale(flare, emerald_orbit(emerald)->scale); flare->color = emerald->color; flare->colorized = true; @@ -265,7 +267,7 @@ static void spawn_lens_flare(mobj_t *emerald) // FIXME: linkdraw doesn't work consistently, so I drew it on top of everyting (and through walls) #if 0 - P_SetTarget(&flare->tracer, emerald->target); + P_SetTarget(&flare->tracer, emerald_orbit(emerald)); flare->flags2 |= MF2_LINKDRAW; flare->dispoffset = 1000; #endif @@ -273,7 +275,12 @@ static void spawn_lens_flare(mobj_t *emerald) void Obj_BeginEmeraldOrbit(mobj_t *emerald, mobj_t *target, fixed_t radius, INT32 revolution_time, tic_t fuse) { - P_SetTarget(&emerald->target, target); + P_SetTarget(&emerald_orbit(emerald), target); + + if (P_MobjWasRemoved(emerald_award(emerald))) + { + P_SetTarget(&emerald_award(emerald), target); + } emerald_anim_start(emerald) = leveltime; emerald_revolution_time(emerald) = revolution_time; @@ -297,14 +304,9 @@ 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->target)) - { - return; - } - - player_t *player = emerald->target->player; + player_t *player = emerald_award(emerald)->player; if (!player) { @@ -314,5 +316,51 @@ void Obj_GiveEmerald(mobj_t *emerald) player->emeralds |= emerald_type(emerald); K_CheckEmeralds(player); - S_StartSound(emerald->target, emerald->info->deathsound); + 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); +} + +boolean Obj_EmeraldCanHUDTrack(const mobj_t *emerald) +{ + if (!P_MobjWasRemoved(emerald_award(emerald)) && emerald_award(emerald)->type == MT_ITEMCAPSULE) + { + return false; + } + + return true; } diff --git a/src/objects/hyudoro.c b/src/objects/hyudoro.c index 4b104b9d0..f0573b27a 100644 --- a/src/objects/hyudoro.c +++ b/src/objects/hyudoro.c @@ -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,18 +214,28 @@ project_hyudoro_hover (mobj_t *hyu) bob_in_place(hyu, 64); } -static void -spawn_hyudoro_shadow (mobj_t *hyu) +static boolean +project_hyudoro_orbit (mobj_t *hyu) { - mobj_t *shadow = P_SpawnMobjFromMobj( - hyu, 0, 0, 0, MT_SHADOW); + mobj_t *orbit = hyudoro_target(hyu); - shadow->whiteshadow = true; + if (P_MobjWasRemoved(orbit)) + { + return false; + } - shadow->shadowscale = hyu->shadowscale; - hyu->shadowscale = 0; + P_MoveOrigin(hyu, orbit->x, orbit->y, orbit->z); + hyu->destscale = orbit->scale; - P_SetTarget(&shadow->tracer, hyu); + 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 * @@ -251,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))); @@ -267,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 @@ -304,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 @@ -351,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 @@ -359,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)) { @@ -409,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 @@ -443,6 +562,8 @@ hyudoro_patrol_hit_player if (player->hyudorotimer) return false; + player->pflags |= PF_CASTSHADOW; + // NO ITEM? if (!player->itemamount) return false; @@ -453,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); @@ -473,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; } @@ -488,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); @@ -544,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) { @@ -572,7 +798,7 @@ Obj_InitHyudoroCenter (mobj_t * center, mobj_t * master) hyu->renderflags &= ~(K_GetPlayerDontDrawFlag(master->player)); } - spawn_hyudoro_shadow(hyu); // this sucks btw + Obj_SpawnFakeShadow(hyu); // this sucks btw } void @@ -584,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 @@ -656,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; +} diff --git a/src/objects/shadow.cpp b/src/objects/shadow.cpp new file mode 100644 index 000000000..58c670ca0 --- /dev/null +++ b/src/objects/shadow.cpp @@ -0,0 +1,109 @@ +#include +#include + +#include "../info.h" +#include "../k_objects.h" +#include "../p_local.h" +#include "../p_mobj.h" +#include "../p_tick.h" +#include "../typedef.h" + +#define shadow_follow(o) ((o)->tracer) + +struct Shadow : mobj_t +{ + mobj_t* follow() const { return shadow_follow(this); } + void follow(mobj_t* n) { P_SetTarget(&shadow_follow(this), n); } + + static Shadow* spawn(mobj_t* from) + { + return static_cast(P_SpawnMobjFromMobj(from, 0, 0, 0, MT_SHADOW))->init(from); + } + + bool valid() const { return !P_MobjWasRemoved(this) && !P_MobjWasRemoved(follow()); } + + std::optional> 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() + { + whiteshadow = follow()->whiteshadow; + shadowcolor = follow()->shadowcolor; + + P_MoveOrigin(this, follow()->x, follow()->y, P_GetMobjFeet(follow())); + } + +private: + Shadow* init(mobj_t* from) + { + shadowscale = from->shadowscale; + from->shadowscale = 0; + + follow(from); + + return this; + } +}; + +mobj_t* Obj_SpawnFakeShadow(mobj_t* from) +{ + return Shadow::spawn(from); +} + +void Obj_FakeShadowThink(mobj_t* shadow) +{ + auto x = static_cast(shadow); + + if (!x->valid()) + { + x->destroy(); + return; + } + + x->move(); +} + +boolean Obj_FakeShadowZ(const mobj_t* shadow, fixed_t* return_z, pslope_t** return_slope) +{ + auto x = static_cast(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; +} diff --git a/src/p_inter.c b/src/p_inter.c index ffb4cff53..732a89dc7 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -115,7 +115,7 @@ void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End) // boolean P_CanPickupItem(player_t *player, UINT8 weapon) { - if (player->exiting || mapreset || (player->pflags & PF_ELIMINATED)) + if (player->exiting || mapreset || (player->pflags & PF_ELIMINATED) || player->itemRoulette.reserved) return false; // 0: Sphere/Ring @@ -165,6 +165,13 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) // 1 = floating item, 2 = perma ring, 3 = capsule boolean P_IsPickupCheesy(player_t *player, UINT8 type) { + extern consvar_t cv_debugcheese; + + if (cv_debugcheese.value) + { + return false; + } + if (player->lastpickupdistance && player->lastpickuptype == type) { UINT32 distancedelta = min(player->distancetofinish - player->lastpickupdistance, player->lastpickupdistance - player->distancetofinish); @@ -1680,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; @@ -1745,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 @@ -2294,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); } diff --git a/src/p_mobj.c b/src/p_mobj.c index 4f1b97657..3a6176d7b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4371,7 +4371,8 @@ static void P_ItemCapsulePartThinker(mobj_t *mobj) else // alive { mobj_t *target = mobj->target; - fixed_t targetScale, z; + fixed_t targetScale; + fixed_t x, y, z; if (P_MobjWasRemoved(target)) { @@ -4394,19 +4395,23 @@ static void P_ItemCapsulePartThinker(mobj_t *mobj) else K_GenericExtraFlagsNoZAdjust(mobj, target); + x = target->x + target->sprxoff; + y = target->y + target->spryoff; + z = target->z + target->sprzoff; + if (mobj->eflags & MFE_VERTICALFLIP) - z = target->z + target->height - mobj->height - FixedMul(mobj->scale, mobj->movefactor); + z += target->height - mobj->height - FixedMul(mobj->scale, mobj->movefactor); else - z = target->z + FixedMul(mobj->scale, mobj->movefactor); + z += FixedMul(mobj->scale, mobj->movefactor); // rotate & move to capsule mobj->angle += mobj->movedir; if (mobj->flags2 & MF2_CLASSICPUSH) // centered - P_MoveOrigin(mobj, target->x, target->y, z); + P_MoveOrigin(mobj, x, y, z); else P_MoveOrigin(mobj, - target->x + P_ReturnThrustX(mobj, mobj->angle + ANGLE_90, mobj->radius), - target->y + P_ReturnThrustY(mobj, mobj->angle + ANGLE_90, mobj->radius), + x + P_ReturnThrustX(mobj, mobj->angle + ANGLE_90, mobj->radius), + y + P_ReturnThrustY(mobj, mobj->angle + ANGLE_90, mobj->radius), z); } } @@ -4438,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; @@ -4482,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: @@ -5782,16 +5792,10 @@ static void P_MobjSceneryThink(mobj_t *mobj) switch (mobj->type) { case MT_SHADOW: - if (mobj->tracer) + Obj_FakeShadowThink(mobj); + + if (P_MobjWasRemoved(mobj)) { - P_MoveOrigin(mobj, - mobj->tracer->x, - mobj->tracer->y, - mobj->tracer->z); - } - else - { - P_RemoveMobj(mobj); return; } break; @@ -7297,6 +7301,14 @@ static boolean P_MobjRegularThink(mobj_t *mobj) break; } case MT_ITEMCAPSULE: + if (!P_MobjWasRemoved(mobj->target)) + { + P_MoveOrigin(mobj, + mobj->target->x + mobj->target->sprxoff, + mobj->target->y + mobj->target->spryoff, + mobj->target->z + mobj->target->sprzoff); + } + // scale the capsule if (mobj->scale < mobj->extravalue1) { @@ -10408,6 +10420,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) { thing->shadowscale = 0; thing->whiteshadow = ((thing->frame & FF_BRIGHTMASK) == FF_FULLBRIGHT); + thing->shadowcolor = 15; switch (thing->type) { diff --git a/src/p_mobj.h b/src/p_mobj.h index 61bf62bf7..bf07a567c 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -415,6 +415,7 @@ struct mobj_t fixed_t shadowscale; // If this object casts a shadow, and the size relative to radius boolean whiteshadow; // Use white shadow, set to true by default for fullbright objects + UINT8 shadowcolor; // Palette index to use for rendering the shadow fixed_t sprxoff, spryoff, sprzoff; // Sprite offsets in real space, does NOT affect position or collision diff --git a/src/p_saveg.c b/src/p_saveg.c index 7f2410694..7dafce936 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -628,6 +628,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].itemRoulette.eggman); WRITEUINT8(save->p, players[i].itemRoulette.ringbox); WRITEUINT8(save->p, players[i].itemRoulette.autoroulette); + WRITEUINT8(save->p, players[i].itemRoulette.reserved); // sonicloopsvars_t WRITEFIXED(save->p, players[i].loop.radius); @@ -1114,6 +1115,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].itemRoulette.eggman = (boolean)READUINT8(save->p); players[i].itemRoulette.ringbox = (boolean)READUINT8(save->p); players[i].itemRoulette.autoroulette = (boolean)READUINT8(save->p); + players[i].itemRoulette.reserved = READUINT8(save->p); // sonicloopsvars_t players[i].loop.radius = READFIXED(save->p); @@ -3023,6 +3025,7 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 { WRITEFIXED(save->p, mobj->shadowscale); WRITEUINT8(save->p, mobj->whiteshadow); + WRITEUINT8(save->p, mobj->shadowcolor); } if (diff2 & MD2_RENDERFLAGS) { @@ -4249,6 +4252,7 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) { mobj->shadowscale = READFIXED(save->p); mobj->whiteshadow = READUINT8(save->p); + mobj->shadowcolor = READUINT8(save->p); } if (diff2 & MD2_RENDERFLAGS) mobj->renderflags = READUINT32(save->p); diff --git a/src/r_draw.c b/src/r_draw.c index b236e749e..42f3473df 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -128,6 +128,8 @@ UINT8 *ds_source; // points to the start of a flat UINT8 *ds_brightmap; // start of brightmap flat UINT8 *ds_transmap; // one of the translucency tables +UINT8 dc_shadowcolor; + // Vectors for Software's tilted slope drawers floatv3_t *ds_su, *ds_sv, *ds_sz; floatv3_t *ds_sup, *ds_svp, *ds_szp; diff --git a/src/r_draw.h b/src/r_draw.h index 4b4df535c..c14747eed 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -59,6 +59,8 @@ extern INT32 dc_numlights, dc_maxlights; //Fix TUTIFRUTI extern INT32 dc_texheight; +extern UINT8 dc_shadowcolor; + // ----------------------- // SPAN DRAWING CODE STUFF // ----------------------- diff --git a/src/r_draw8.c b/src/r_draw8.c index 8f623286f..e83eea875 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -584,9 +584,7 @@ void R_DrawDropShadowColumn_8(void) dest = &topleft[dc_yl*vid.width + dc_x]; { -#define DSCOLOR 15 // palette index for the color of the shadow - register const UINT8 *transmap_offset = dc_transmap + (dc_colormap[DSCOLOR] << 8); -#undef DSCOLOR + register const UINT8 *transmap_offset = dc_transmap + (dc_colormap[dc_shadowcolor] << 8); while ((count -= 2) >= 0) { *dest = *(transmap_offset + (*dest)); diff --git a/src/r_spritefx.cpp b/src/r_spritefx.cpp index 756e0a96e..0664819ff 100644 --- a/src/r_spritefx.cpp +++ b/src/r_spritefx.cpp @@ -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; +} diff --git a/src/r_things.c b/src/r_things.c index e597b9b77..d94ed27af 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -926,6 +926,7 @@ static void R_DrawVisSprite(vissprite_t *vis) { R_SetColumnFunc(COLDRAWFUNC_DROPSHADOW, false); dc_transmap = vis->transmap; + dc_shadowcolor = vis->color; } else if (!(vis->cut & SC_PRECIP) && R_ThingIsFlashing(vis->mobj)) // Bosses "flash" @@ -1314,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}; @@ -1543,6 +1552,8 @@ static void R_ProjectDropShadow( shadow->transmap = R_GetBlendTable(thing->whiteshadow ? AST_ADD : AST_SUBTRACT, 0); shadow->colormap = colormaps; + shadow->color = thing->shadowcolor; + objectsdrawn++; } diff --git a/src/r_things.h b/src/r_things.h index 2737ded62..8f74f5135 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -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 @@ -225,6 +226,7 @@ struct vissprite_t fixed_t spritexoffset, spriteyoffset; fixed_t shadowscale; + UINT8 color; // palette index INT16 clipbot[MAXVIDWIDTH], cliptop[MAXVIDWIDTH];