From dd05900bcb494f0d654acc508adbd1622dfb3aee Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 2 Jul 2025 23:05:31 -0700 Subject: [PATCH] Add Toxomister item --- src/cvars.cpp | 1 + src/d_player.h | 4 +- src/deh_tables.c | 8 + src/info.c | 84 ++++++++ src/info.h | 10 + src/k_hud.cpp | 5 + src/k_kart.c | 26 ++- src/k_objects.h | 10 + src/objects/CMakeLists.txt | 1 + src/objects/toxomister.cpp | 427 +++++++++++++++++++++++++++++++++++++ src/p_inter.c | 8 + src/p_map.c | 2 + src/p_mobj.c | 17 ++ src/p_saveg.cpp | 15 ++ src/p_user.c | 1 + 15 files changed, 617 insertions(+), 2 deletions(-) create mode 100644 src/objects/toxomister.cpp diff --git a/src/cvars.cpp b/src/cvars.cpp index 2b30a208d..c74f42254 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -687,6 +687,7 @@ consvar_t cv_items[] = { UnsavedNetVar("gardentop", "On").on_off(), UnsavedNetVar("gachabom", "On").on_off(), UnsavedNetVar("stoneshoe", "On").on_off(), + UnsavedNetVar("toxomister", "On").on_off(), UnsavedNetVar("dualsneaker", "On").on_off(), UnsavedNetVar("triplesneaker", "On").on_off(), UnsavedNetVar("triplebanana", "On").on_off(), diff --git a/src/d_player.h b/src/d_player.h index d440ecbfe..8552ddf26 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -199,7 +199,8 @@ Run this macro, then #undef FOREACH afterward FOREACH (DROPTARGET, 21),\ FOREACH (GARDENTOP, 22),\ FOREACH (GACHABOM, 23),\ - FOREACH (STONESHOE, 24) + FOREACH (STONESHOE, 24),\ + FOREACH (TOXOMISTER, 25) typedef enum { @@ -1066,6 +1067,7 @@ struct player_t mobj_t *hand; mobj_t *flickyAttacker; mobj_t *stoneShoe; + mobj_t *toxomisterCloud; SINT8 pitblame; // Index of last player that hit you, resets after being in control for a bit. If you deathpit, credit the old attacker! diff --git a/src/deh_tables.c b/src/deh_tables.c index a880a1766..91e53e39a 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3120,6 +3120,10 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_FLYBOT767", "S_STON", + + "S_TOXAA", + "S_TOXAB", + "S_TOXBA", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -4028,6 +4032,10 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_STONESHOE", "MT_STONESHOE_CHAIN", + + "MT_TOXOMISTER_POLE", + "MT_TOXOMISTER_EYE", + "MT_TOXOMISTER_CLOUD", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index 2e6e1ac72..8eb8deeba 100644 --- a/src/info.c +++ b/src/info.c @@ -801,6 +801,8 @@ char sprnames[NUMSPRITES + 1][5] = "STUN", "STON", + "TOXA", + "TOXB", // Pulley "HCCH", @@ -3704,6 +3706,10 @@ state_t states[NUMSTATES] = {SPR_STUN, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 4, 4, S_NULL}, // S_FLYBOT767 {SPR_STON, 0, -1, {NULL}, 0, 0, S_STON}, // S_STON + // + {SPR_TOXA, 0, -1, {NULL}, 0, 0, S_TOXAA}, // S_TOXAA + {SPR_TOXA, 1, -1, {NULL}, 0, 0, S_TOXAB}, // S_TOXAB + {SPR_TOXB, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 6, 5, S_TOXBA}, // S_TOXBA }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -22712,6 +22718,84 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_SPECIAL|MF_SCENERY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_PICKUPFROMBELOW|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, + { // MT_TOXOMISTER_POLE + -1, // doomednum + S_TOXAA, // spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_tossed, // seesound + 8, // 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 + 64*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SHOOTABLE|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_TOXOMISTER_EYE + -1, // doomednum + S_TOXAB, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // 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 + 64*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_TOXOMISTER_CLOUD + -1, // doomednum + S_TOXBA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // 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 + 70*FRACUNIT, // radius + 70*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, }; diff --git a/src/info.h b/src/info.h index 996b721e2..da59483bf 100644 --- a/src/info.h +++ b/src/info.h @@ -1338,6 +1338,8 @@ typedef enum sprite SPR_STUN, SPR_STON, + SPR_TOXA, + SPR_TOXB, // Pulley SPR_HCCH, @@ -4188,6 +4190,10 @@ typedef enum state S_STON, + S_TOXAA, + S_TOXAB, + S_TOXBA, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -5119,6 +5125,10 @@ typedef enum mobj_type MT_STONESHOE, MT_STONESHOE_CHAIN, + MT_TOXOMISTER_POLE, + MT_TOXOMISTER_EYE, + MT_TOXOMISTER_CLOUD, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 5bfc23d9f..eaa3c0030 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -172,6 +172,7 @@ static patch_t *kp_droptarget[3]; static patch_t *kp_gardentop[3]; static patch_t *kp_gachabom[3]; static patch_t *kp_stoneshoe[3]; +static patch_t *kp_toxomister[3]; static patch_t *kp_bar[2]; static patch_t *kp_doublebar[2]; static patch_t *kp_triplebar[2]; @@ -649,6 +650,7 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_gardentop[0], "K_ITGTOP"); HU_UpdatePatch(&kp_gachabom[0], "K_ITGBOM"); HU_UpdatePatch(&kp_stoneshoe[0], "K_ITSTON"); + HU_UpdatePatch(&kp_toxomister[0], "K_ITTOX"); HU_UpdatePatch(&kp_bar[0], "K_RBBAR"); HU_UpdatePatch(&kp_doublebar[0], "K_RBBAR2"); HU_UpdatePatch(&kp_triplebar[0], "K_RBBAR3"); @@ -710,6 +712,7 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_gardentop[1], "K_ISGTOP"); HU_UpdatePatch(&kp_gachabom[1], "K_ISGBOM"); HU_UpdatePatch(&kp_stoneshoe[1], "K_ISSTON"); + HU_UpdatePatch(&kp_toxomister[1], "K_ISTOX"); HU_UpdatePatch(&kp_bar[1], "K_SBBAR"); HU_UpdatePatch(&kp_doublebar[1], "K_SBBAR2"); HU_UpdatePatch(&kp_triplebar[1], "K_SBBAR3"); @@ -769,6 +772,7 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_gardentop[2], "ISPYGTOP"); HU_UpdatePatch(&kp_gachabom[2], "ISPYGBOM"); HU_UpdatePatch(&kp_stoneshoe[2], "ISPYSTON"); + HU_UpdatePatch(&kp_toxomister[2], "ISPYTOX"); // CHECK indicators sprintf(buffer, "K_CHECKx"); @@ -1190,6 +1194,7 @@ static patch_t *K_GetCachedItemPatch(INT32 item, UINT8 offset) kp_gardentop, kp_gachabom, kp_stoneshoe, + kp_toxomister, }; if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS)) diff --git a/src/k_kart.c b/src/k_kart.c index 6e395a97f..5028c6624 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4143,6 +4143,11 @@ fixed_t K_GetNewSpeed(const player_t *player) p_speed = 15 * p_speed / 10; } + if (!P_MobjWasRemoved(player->toxomisterCloud)) + { + p_speed = FixedMul(p_speed, Obj_GetToxomisterCloudDrag(player->toxomisterCloud)); + } + if (K_PlayerUsesBotMovement(player) == true && player->botvars.rubberband > 0) { // Acceleration is tied to top speed... @@ -7076,7 +7081,7 @@ mobj_t *K_ThrowKartItemEx(player_t *player, boolean missile, mobjtype_t mapthing { mobj_t *lasttrail = K_FindLastTrailMobj(player); - if (mapthing == MT_BUBBLESHIELDTRAP) // Drop directly on top of you. + if (mapthing == MT_BUBBLESHIELDTRAP || mapthing == MT_TOXOMISTER_POLE) // Drop directly on top of you. { newangle = player->mo->angle; newx = player->mo->x + player->mo->momx; @@ -15069,6 +15074,21 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->botvars.itemconfirm = 0; } break; + case KITEM_TOXOMISTER: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_SetItemOut(player); // need this to set itemscale + + mobj_t *pole = K_ThrowKartItem(player, false, MT_TOXOMISTER_POLE, -1, 0, 0); + Obj_InitToxomisterPole(pole); + + K_UnsetItemOut(player); + + player->itemamount--; + K_PlayAttackTaunt(player->mo); + player->botvars.itemconfirm = 0; + } + break; case KITEM_SAD: if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO && !player->sadtimer) @@ -16363,6 +16383,7 @@ boolean K_IsPickMeUpItem(mobjtype_t type) case MT_SSMINE: case MT_SSMINE_SHIELD: case MT_FLOATINGITEM: // Stone Shoe + case MT_TOXOMISTER_POLE: return true; default: return false; @@ -16425,6 +16446,9 @@ static boolean K_PickUp(player_t *player, mobj_t *picked) else type = KITEM_SAD; break; + case MT_TOXOMISTER_POLE: + type = KITEM_TOXOMISTER; + break; default: type = KITEM_SAD; break; diff --git a/src/k_objects.h b/src/k_objects.h index c894c0f18..d29dc74cb 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -475,6 +475,16 @@ boolean Obj_TickStoneShoeChain(mobj_t *chain); player_t *Obj_StoneShoeOwnerPlayer(mobj_t *shoe); void Obj_CollideStoneShoe(mobj_t *mover, mobj_t *mobj); +/* Toxomister */ +void Obj_InitToxomisterPole(mobj_t *pole); +boolean Obj_TickToxomisterPole(mobj_t *pole); +boolean Obj_TickToxomisterEye(mobj_t *eye); +boolean Obj_TickToxomisterCloud(mobj_t *cloud); +boolean Obj_ToxomisterPoleCollide(mobj_t *pole, mobj_t *toucher); +boolean Obj_ToxomisterCloudCollide(mobj_t *cloud, mobj_t *toucher); +fixed_t Obj_GetToxomisterCloudDrag(mobj_t *cloud); + + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 7a9cb347e..bb5205738 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -67,6 +67,7 @@ target_sources(SRB2SDL2 PRIVATE stone-shoe.cpp exp.c bail.c + toxomister.cpp ) add_subdirectory(versus) diff --git a/src/objects/toxomister.cpp b/src/objects/toxomister.cpp new file mode 100644 index 000000000..b99bb6b0a --- /dev/null +++ b/src/objects/toxomister.cpp @@ -0,0 +1,427 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by James Robert Roman +// Copyright (C) 2025 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 +#include + +#include "objects.hpp" + +#include "../core/static_vec.hpp" +#include "../d_player.h" +#include "../doomdef.h" +#include "../doomtype.h" +#include "../g_game.h" +#include "../k_hud.h" // transflag +#include "../m_easing.h" +#include "../m_fixed.h" +#include "../m_random.h" +#include "../r_main.h" +#include "../tables.h" + +using namespace srb2::objects; + +namespace +{ + +Fixed distance3d(const Mobj* a, const Mobj* b) +{ + return FixedHypot(FixedHypot(a->x - b->x, a->y - b->y), a->z - b->z); +} + +Vec2 angle_vector(angle_t x) +{ + return Vec2 {FCOS(x), FSIN(x)}; +} + +// copied from objects/hyudoro.c +static void +sine_bob +( mobj_t * hyu, + INT32 height, + angle_t a, + fixed_t sineofs) +{ + hyu->sprzoff = FixedMul(height * hyu->scale, + sineofs + FINESINE(a >> ANGLETOFINESHIFT)) * P_MobjFlip(hyu); + + if (P_IsObjectFlipped(hyu)) + hyu->sprzoff -= hyu->height; +} + +static void +bob_in_place +( mobj_t * hyu, + INT32 height, + INT32 bob_speed) +{ + sine_bob(hyu, + height, + (leveltime & (bob_speed - 1)) * + (ANGLE_MAX / bob_speed), -(3*FRACUNIT/4)); +} + +struct Eye; +struct Pole; +struct Cloud; + +struct Eye : Mobj +{ + static constexpr INT32 kOrbitRadius = 24; + + bool valid() const { return Mobj::valid(owner()); } + + bool tick() + { + if (!valid()) + { + remove(); + return false; + } + + return true; + } +}; + +struct Pole : Mobj +{ + static constexpr sfxenum_t kSound = sfx_s3kdal; + + void extravalue1() = delete; + tic_t last_touch0() const { return mobj_t::extravalue1; } + void last_touch0(tic_t n) { mobj_t::extravalue1 = n; } + + void extravalue2() = delete; + bool clouds_spawned() const { return mobj_t::extravalue2; } + void clouds_spawned(bool n) { mobj_t::extravalue2 = n; } + + void reactiontime() = delete; + tic_t sound_started() const { return mobj_t::reactiontime; } + void sound_started(tic_t n) { mobj_t::reactiontime = n; } + + void tracer() = delete; + Eye* eye() const { return Mobj::tracer(); } + void eye(Eye* n) { Mobj::tracer(n); } + + bool valid() const + { + if (!Mobj::valid(eye())) + return false; + + return true; + } + + void init() + { + Eye* p_eye = spawn_from(MT_TOXOMISTER_EYE); + + p_eye->owner(this); + p_eye->spriteyoffset(96*FRACUNIT); + + last_touch0(leveltime); + clouds_spawned(false); + eye(p_eye); + + flags |= MF_SPECIAL; + } + + void spawn_clouds_in_orbit(); + + bool tick() + { + if (!valid()) + { + remove(); + return false; + } + + if (P_IsObjectOnGround(this)) + { + if (!clouds_spawned()) + { + spawn_clouds_in_orbit(); + clouds_spawned(true); + voice(sfx_s3k9e); + } + + if (!voice_playing(kSound)) + { + voice(kSound); + sound_started(leveltime); + } + + if ((leveltime - sound_started()) % 256 == 0) + voice(kSound); + } + else + { + P_SpawnGhostMobj(this); + } + + tick_eye(); + + return true; + } + + void tick_eye() + { + Mobj::PosArg p = {pos2d(), z}; + + p.x += momx; + p.y += momy; + p.z += momz; + + Mobj* targ = find_nearest_eyeball_target(); + if (targ) + { + INT32 angle_to_targ = angle_to2d(targ); + Vec2 v = angle_vector(angle_to_targ) * Fixed {Eye::kOrbitRadius * mapobjectscale}; + + p.x += v.x; + p.y += v.y; + + eye()->angle = angle_to_targ; + } + + eye()->move_origin(p); + } + + angle_t angle_to2d(Mobj* mobj) const + { + return R_PointToAngle2(x, y, mobj->x, mobj->y); + } + + Mobj* find_nearest_eyeball_target() const + { + srb2::StaticVec targets; + + for (INT32 i = 0; i < MAXPLAYERS; ++i) + { + if (!playeringame[i]) + continue; + + if (!players[i].mo) + continue; + + targets.push_back(static_cast(players[i].mo)); + } + + if (targets.empty()) + return nullptr; + + return *std::min_element( + targets.begin(), + targets.end(), + [this](Mobj* a, Mobj* b) { return distance3d(this, a) < distance3d(this, b); } + ); + } + + bool touch(Mobj* toucher) + { + if (touch_cooldown(toucher, 0)) + return false; + + if (K_TryPickMeUp(this, toucher, false)) + return false; + + // TODO: spawn a puff of smoke? + remove(); + + return false; + } + + bool touch_cooldown + ( Mobj* toucher, + UINT8 k) + { + tic_t cooldown = leveltime - last_touch0(); + + if (toucher == target() && cooldown < 10) + { + last_touch0(leveltime); + return true; + } + + return false; + } +}; + +struct Cloud : Mobj +{ + static constexpr INT32 kMaxFuse = 5*TICRATE; + + void hnext() = delete; + Mobj* follow() const { return Mobj::hnext(); } + void follow(Mobj* n) { Mobj::hnext(n); } + + void tracer() = delete; + Pole* pole() const { return Mobj::tracer(); } + void pole(Pole* n) { Mobj::tracer(n); } + + Fixed fuse_frac() const { return FRACUNIT - fuse * FRACUNIT / kMaxFuse; } + Fixed drag_var() const { return Easing_Linear(fuse_frac(), FRACUNIT/3, FRACUNIT); } + + bool tick() + { + if (Mobj::valid(follow())) + return tick_follow(); + + return tick_patrol(); + } + + bool tick_follow() + { + if (!Mobj::valid(follow())) + { + remove(); + return false; + } + + move_origin(follow()->pos()); + momx = 0; + momy = 0; + momz = 0; + + bob_in_place(this, 8, 64); + voice_loop(sfx_s3kcfl); + + if (fuse < 3*TICRATE && leveltime % (1 + fuse / TICRATE) == 0) + { + renderflags ^= RF_DONTDRAW; + } + + if (fuse < kMaxFuse && (kMaxFuse - fuse) % 20 == 0 && Mobj::valid(target()) && target()->player && follow()->player) + { + K_SpawnAmps(target()->player, K_PvPAmpReward(5, target()->player, follow()->player), this); + } + + return true; + } + + bool tick_patrol() + { + if (Mobj::valid(pole())) + { + move_origin(pole()->pos()); + instathrust(angle, 64 * mapobjectscale); + } + else + { + if (!fuse) + { + fuse = 3*TICRATE; + instathrust(angle, 2 * mapobjectscale); + } + + if (leveltime & 1) + { + renderflags ^= RF_DONTDRAW; + } + } + + return true; + } + + bool touch(Mobj* toucher) + { + if (toucher == target()) + return false; + + if (toucher->player) + { + if (this == toucher->player->toxomisterCloud) // already attached + return true; + + if (!P_MobjWasRemoved(toucher->player->toxomisterCloud)) + { + toucher->player->pflags |= PF_CASTSHADOW; + return true; + } + + P_SetTarget(&toucher->player->toxomisterCloud, this); + } + + toucher->hitlag(8); + scale_to(destscale); + follow(toucher); + fuse = kMaxFuse; + voice(sfx_s3ka0); + + return true; + } +}; + +void Pole::spawn_clouds_in_orbit() +{ + constexpr INT32 kNumClouds = 6; + std::array weights; + std::array order; + + angle_t a = 0; + angle_t a_incr = ANGLE_MAX / kNumClouds; + + for (INT32 i = 0; i < kNumClouds; ++i) + { + weights[i] = P_Random(PR_TRACKHAZARD); + order[i] = i; + } + + std::stable_sort(order.begin(), order.end(), [&](INT32 a, INT32 b) { return weights[a] < weights[b]; }); + + for (INT32 i : order) + { + Cloud* cloud = spawn_from({}, MT_TOXOMISTER_CLOUD); + + cloud->pole(this); + cloud->angle = a; + cloud->target(target()); + cloud->spriteyoffset(24*FRACUNIT); + cloud->hitlag(2 + i * 4); + cloud->scale_between(1, cloud->scale(), cloud->scale() / 5); + + a += a_incr; + } +} + +}; // namespace + +void Obj_InitToxomisterPole(mobj_t *pole) +{ + static_cast(pole)->init(); +} + +boolean Obj_TickToxomisterPole(mobj_t *pole) +{ + return static_cast(pole)->tick(); +} + +boolean Obj_TickToxomisterEye(mobj_t *eye) +{ + return static_cast(eye)->tick(); +} + +boolean Obj_TickToxomisterCloud(mobj_t *cloud) +{ + return static_cast(cloud)->tick(); +} + +boolean Obj_ToxomisterPoleCollide(mobj_t *pole, mobj_t *toucher) +{ + return static_cast(pole)->touch(static_cast(toucher)); +} + +boolean Obj_ToxomisterCloudCollide(mobj_t *cloud, mobj_t *toucher) +{ + return static_cast(cloud)->touch(static_cast(toucher)); +} + +fixed_t Obj_GetToxomisterCloudDrag(mobj_t *cloud) +{ + return static_cast(cloud)->drag_var(); +} diff --git a/src/p_inter.c b/src/p_inter.c index c8ba97f2b..8b3e6e25a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1129,6 +1129,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) Obj_CollideStoneShoe(toucher, special); return; + case MT_TOXOMISTER_POLE: + Obj_ToxomisterPoleCollide(special, toucher); + return; + + case MT_TOXOMISTER_CLOUD: + Obj_ToxomisterCloudCollide(special, toucher); + return; + default: // SOC or script pickup P_SetTarget(&special->target, toucher); break; diff --git a/src/p_map.c b/src/p_map.c index 76f3d80bc..414296f2c 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1025,6 +1025,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) || g_tm.thing->type == MT_MONITOR || g_tm.thing->type == MT_BATTLECAPSULE || g_tm.thing->type == MT_KART_LEFTOVER + || g_tm.thing->type == MT_TOXOMISTER_POLE || (g_tm.thing->type == MT_PLAYER))) { // see if it went over / under @@ -1043,6 +1044,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) || thing->type == MT_MONITOR || thing->type == MT_BATTLECAPSULE || thing->type == MT_KART_LEFTOVER + || thing->type == MT_TOXOMISTER_POLE || (thing->type == MT_PLAYER))) { // see if it went over / under diff --git a/src/p_mobj.c b/src/p_mobj.c index 21e0eb317..285e05676 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1246,6 +1246,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo) gravityadd /= 2; break; case MT_GACHABOM: + case MT_TOXOMISTER_POLE: gravityadd = (5*gravityadd)/2; break; case MT_BANANA: @@ -2337,6 +2338,7 @@ boolean P_ZMovement(mobj_t *mo) case MT_BIGTUMBLEWEED: case MT_LITTLETUMBLEWEED: case MT_EMERALD: + case MT_TOXOMISTER_POLE: if (!(mo->flags & MF_NOCLIPHEIGHT) && P_CheckDeathPitCollide(mo)) { P_RemoveMobj(mo); @@ -5320,6 +5322,7 @@ boolean P_IsKartItem(INT32 type) case MT_HYUDORO: case MT_SINK: case MT_GACHABOM: + case MT_TOXOMISTER_POLE: return true; default: @@ -5346,6 +5349,7 @@ boolean P_IsKartFieldItem(INT32 type) case MT_DROPTARGET: case MT_DUELBOMB: case MT_GACHABOM: + case MT_TOXOMISTER_POLE: return true; default: @@ -5379,6 +5383,7 @@ boolean P_IsRelinkItem(INT32 type) case MT_HYUDORO_CENTER: case MT_SINK: case MT_GACHABOM: + case MT_TOXOMISTER_POLE: return true; default: @@ -10295,6 +10300,15 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_STONESHOE: return Obj_TickStoneShoe(mobj); + case MT_TOXOMISTER_POLE: + return Obj_TickToxomisterPole(mobj); + + case MT_TOXOMISTER_EYE: + return Obj_TickToxomisterEye(mobj); + + case MT_TOXOMISTER_CLOUD: + return Obj_TickToxomisterCloud(mobj); + default: // check mobj against possible water content, before movement code P_MobjCheckWater(mobj); @@ -11159,6 +11173,9 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) case MT_STONESHOE_CHAIN: thing->shadowscale = FRACUNIT/5; break; + case MT_TOXOMISTER_POLE: + thing->shadowscale = FRACUNIT; + break; default: if (thing->flags & (MF_ENEMY|MF_BOSS)) thing->shadowscale = FRACUNIT; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 2351cc64b..41463eead 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -93,6 +93,7 @@ typedef enum BALLHOGRETICULE = 0x8000, STONESHOE = 0x10000, FLYBOT = 0x20000, + TOXOMISTERCLOUD = 0x40000, } player_saveflags; static inline void P_ArchivePlayer(savebuffer_t *save) @@ -368,6 +369,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (players[i].stoneShoe) flags |= STONESHOE; + if (players[i].toxomisterCloud) + flags |= TOXOMISTERCLOUD; + if (players[i].flybot) flags |= FLYBOT; @@ -421,6 +425,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (flags & STONESHOE) WRITEUINT32(save->p, players[i].stoneShoe->mobjnum); + if (flags & TOXOMISTERCLOUD) + WRITEUINT32(save->p, players[i].toxomisterCloud->mobjnum); + if (flags & FLYBOT) WRITEUINT32(save->p, players[i].flybot->mobjnum); @@ -1082,6 +1089,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) if (flags & STONESHOE) players[i].stoneShoe = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & TOXOMISTERCLOUD) + players[i].toxomisterCloud = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & FLYBOT) players[i].flybot = (mobj_t *)(size_t)READUINT32(save->p); @@ -6247,6 +6257,11 @@ static void P_RelinkPointers(void) if (!RelinkMobj(&players[i].stoneShoe)) CONS_Debug(DBG_GAMELOGIC, "stoneShoe not found on player %d\n", i); } + if (players[i].toxomisterCloud) + { + if (!RelinkMobj(&players[i].toxomisterCloud)) + CONS_Debug(DBG_GAMELOGIC, "toxomisterCloud not found on player %d\n", i); + } if (players[i].flybot) { if (!RelinkMobj(&players[i].flybot)) diff --git a/src/p_user.c b/src/p_user.c index 5e0635bbf..bf8baf3df 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4259,6 +4259,7 @@ void P_PlayerThink(player_t *player) PlayerPointerErase(player->ballhogreticule); PlayerPointerErase(player->flickyAttacker); PlayerPointerErase(player->stoneShoe); + PlayerPointerErase(player->toxomisterCloud); PlayerPointerErase(player->powerup.flickyController); PlayerPointerErase(player->powerup.barrier);