diff --git a/src/deh_soc.c b/src/deh_soc.c index 53ad24ae6..6309e7cf8 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3294,7 +3294,8 @@ static void readcondition(UINT16 set, UINT32 id, char *word2) || (++offset && fastcmp(params[0], "HITMIDAIR")) || (++offset && fastcmp(params[0], "HITDRAFTERLOOKBACK")) || (++offset && fastcmp(params[0], "GIANTRACERSHRUNKENORBI")) - || (++offset && fastcmp(params[0], "RETURNMARKTOSENDER"))) + || (++offset && fastcmp(params[0], "RETURNMARKTOSENDER")) + || (++offset && fastcmp(params[0], "ALLANCIENTGEARS"))) { //PARAMCHECK(1); ty = UCRP_TRIPWIREHYUU + offset; diff --git a/src/deh_tables.c b/src/deh_tables.c index 653588012..63cae9fbe 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3120,6 +3120,9 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_TOXAA_DEAD", "S_TOXAB", "S_TOXBA", + + "S_ANCIENTGEAR", + "S_ANCIENTGEAR_PART", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -4036,6 +4039,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_TOXOMISTER_POLE", "MT_TOXOMISTER_EYE", "MT_TOXOMISTER_CLOUD", + + "MT_ANCIENTGEAR", + "MT_ANCIENTGEAR_PART", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index 8171d1333..f536475b5 100644 --- a/src/info.c +++ b/src/info.c @@ -812,6 +812,8 @@ char sprnames[NUMSPRITES + 1][5] = "TOXA", "TOXB", + "GEAR", + // Pulley "HCCH", "HCHK", @@ -3713,6 +3715,9 @@ state_t states[NUMSTATES] = {SPR_TOXA, 0, 175, {NULL}, 0, 0, S_NULL}, // S_TOXAA_DEAD {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 + + {SPR_GEAR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_ANCIENTGEAR + {SPR_GEAR, FF_PAPERSPRITE|1, -1, {NULL}, 0, 0, S_NULL}, // S_ANCIENTGEAR_PART }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -22880,6 +22885,58 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_SPECIAL|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_ELEMENTAL, // flags S_NULL // raisestate }, + { // MT_ANCIENTGEAR + 323, // doomednum + S_ANCIENTGEAR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_gotgea, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_ANCIENTGEAR, // deathstate + S_NULL, // xdeathstate + sfx_gshc4, // deathsound + 0, // speed + 64*FRACUNIT, // radius + 128*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_NOSQUISH, // flags + S_NULL // raisestate + }, + { // MT_ANCIENTGEAR_PART + -1, // doomednum + S_ANCIENTGEAR_PART, // 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 + 64*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP|MF_NOSQUISH, // flags + S_NULL // raisestate + }, }; diff --git a/src/info.h b/src/info.h index 9d89ccb84..a207dca6a 100644 --- a/src/info.h +++ b/src/info.h @@ -1349,6 +1349,8 @@ typedef enum sprite SPR_TOXA, SPR_TOXB, + SPR_GEAR, + // Pulley SPR_HCCH, SPR_HCHK, @@ -4197,6 +4199,9 @@ typedef enum state S_TOXAB, S_TOXBA, + S_ANCIENTGEAR, + S_ANCIENTGEAR_PART, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -5136,6 +5141,9 @@ typedef enum mobj_type MT_TOXOMISTER_EYE, MT_TOXOMISTER_CLOUD, + MT_ANCIENTGEAR, + MT_ANCIENTGEAR_PART, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_battle.c b/src/k_battle.c index 8d3477da4..b594e0649 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -233,7 +233,8 @@ void K_CheckEmeralds(player_t *player) player->angleturn + ANGLE_180, 400*mapobjectscale, 6*TICRATE, - FRACUNIT/16 + FRACUNIT/16, + 3*TICRATE ); g_emeraldWin += g_endcam.swirlDuration; diff --git a/src/k_endcam.cpp b/src/k_endcam.cpp index 3fb7f65cb..57df5513b 100644 --- a/src/k_endcam.cpp +++ b/src/k_endcam.cpp @@ -131,6 +131,11 @@ struct EndCam : endcam_t } + void Stop() + { + active = false; + } + template void Archive(T&& ar) { @@ -203,7 +208,7 @@ void K_LoadEndCamera(savebuffer_t *save) endcam_cast().Archive(srb2::UnArchiveWrapper(save)); } -void K_StartRoundWinCamera(mobj_t *origin, angle_t focusAngle, fixed_t finalRadius, tic_t panDuration, fixed_t panSpeed) +void K_StartRoundWinCamera(mobj_t *origin, angle_t focusAngle, fixed_t finalRadius, tic_t panDuration, fixed_t panSpeed, tic_t swirlDuration) { const fixed_t angF = AngleFixed(focusAngle); @@ -211,7 +216,7 @@ void K_StartRoundWinCamera(mobj_t *origin, angle_t focusAngle, fixed_t finalRadi g_endcam.startRadius = {2400*mapobjectscale, 800*mapobjectscale}; g_endcam.endRadius = {finalRadius, finalRadius / 2}; - g_endcam.swirlDuration = 3*TICRATE; + g_endcam.swirlDuration = swirlDuration; g_endcam.startAngle = angF + (90*FRACUNIT); g_endcam.endAngle = angF + (720*FRACUNIT); @@ -224,3 +229,8 @@ void K_StartRoundWinCamera(mobj_t *origin, angle_t focusAngle, fixed_t finalRadi g_darkness.start = leveltime; g_darkness.end = leveltime + g_endcam.swirlDuration + DARKNESS_FADE_TIME; } + +void K_StopRoundWinCamera(void) +{ + endcam_cast().Stop(); +} diff --git a/src/k_endcam.h b/src/k_endcam.h index 7807cc88d..bca34e198 100644 --- a/src/k_endcam.h +++ b/src/k_endcam.h @@ -60,7 +60,10 @@ extern endcam_t g_endcam; void K_CommitEndCamera(void); // Automatically set up a cool camera in one-shot. -void K_StartRoundWinCamera(mobj_t *origin, angle_t focusAngle, fixed_t finalRadius, tic_t panDuration, fixed_t panSpeed); +void K_StartRoundWinCamera(mobj_t *origin, angle_t focusAngle, fixed_t finalRadius, tic_t panDuration, fixed_t panSpeed, tic_t swirlDuration); + +// Stop the end camera +void K_StopRoundWinCamera(void); /// ... diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 1ac3b0aa5..a02c09c3a 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -8038,6 +8038,17 @@ void K_drawKartHUD(void) } } + if (stplyr == Obj_GetAncientGearCollectingPlayer()) + { + srb2::Draw(BASEVIDWIDTH / 2, 130) + .flags(V_SNAPTOBOTTOM) + .font(srb2::Draw::Font::kGamemode) + .align(srb2::Draw::Align::kCenter) + .scale(0.80f) + .text("YOU GOT AN ANCIENT GEAR") + ; + } + // TODO better voice chat speaking indicator integration for spectators { char speakingstring[2048]; diff --git a/src/k_kart.c b/src/k_kart.c index d19a45579..38bb6f201 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4589,7 +4589,8 @@ void K_CheckpointCrossAward(player_t *player) player->angleturn + ANGLE_180, 400*mapobjectscale, 6*TICRATE, - FRACUNIT/16 + FRACUNIT/16, + 3*TICRATE ); } @@ -4835,7 +4836,8 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN R_PointToAngle2(source->x, source->y, victim->mo->x, victim->mo->y) + ANGLE_135, 200*mapobjectscale, 8*TICRATE, - FRACUNIT/512 + FRACUNIT/512, + 3*TICRATE ); } diff --git a/src/k_objects.h b/src/k_objects.h index 402ab2991..41a8e4462 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -488,6 +488,17 @@ 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); +/* Ancient Gear */ +void Obj_AncientGearSpawn(mobj_t *gear); +void Obj_AncientGearPartThink(mobj_t *part); +void Obj_AncientGearRemoved(mobj_t *gear); +void Obj_AncientGearTouch(mobj_t *gear, mobj_t *toucher); +void Obj_AncientGearDeath(mobj_t *gear, mobj_t *source); +void Obj_AncientGearDeadThink(mobj_t *gear); +void Obj_AncientGearLevelInit(void); +player_t *Obj_GetAncientGearCollectingPlayer(void); +boolean Obj_AllAncientGearsCollected(void); + #ifdef __cplusplus } // extern "C" diff --git a/src/m_cheat.c b/src/m_cheat.c index e78e56cb9..e0babc317 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -668,7 +668,9 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c mt->angle = (INT16)(FixedInt(AngleFixed(player->mo->angle))); mt->options = (mt->z << ZSHIFT) | (UINT16)cv_opflags.value; - mt->scale = player->mo->scale; + mt->scale = FixedDiv(player->mo->scale, mapobjectscale); + mt->spritexscale = FRACUNIT; + mt->spriteyscale = FRACUNIT; memset(mt->thing_args, 0, NUM_MAPTHING_ARGS*sizeof(*mt->thing_args)); memset(mt->thing_stringargs, 0x00, NUM_MAPTHING_STRINGARGS*sizeof(*mt->thing_stringargs)); mt->special = 0; diff --git a/src/m_cond.c b/src/m_cond.c index 7fc3df7db..1bda2ffe0 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -33,6 +33,7 @@ #include "k_podium.h" #include "k_pwrlv.h" #include "k_profiles.h" +#include "k_objects.h" // Obj_AllAncientGearsCollected gamedata_t *gamedata = NULL; boolean netUnlocked[MAXUNLOCKABLES]; @@ -2035,6 +2036,8 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) return (player->roundconditions.giant_foe_shrunken_orbi); case UCRP_RETURNMARKTOSENDER: return (player->roundconditions.returntosender_mark); + case UCRP_ALLANCIENTGEARS: + return Obj_AllAncientGearsCollected(); case UCRP_TRACKHAZARD: { @@ -2948,6 +2951,8 @@ static const char *M_GetConditionString(condition_t *cn) return "hit a giant racer with a shrunken Orbinaut"; case UCRP_RETURNMARKTOSENDER: return "when cursed with Eggmark, blow up the racer responsible"; + case UCRP_ALLANCIENTGEARS: + return "collect all Ancient Gears"; case UCRP_TRACKHAZARD: { diff --git a/src/m_cond.h b/src/m_cond.h index 09175e8b3..548cbae60 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -148,6 +148,7 @@ typedef enum UCRP_HITDRAFTERLOOKBACK, // Hit a player that's behind you, while looking back at them, and they're drafting off you UCRP_GIANTRACERSHRUNKENORBI, // Hit a giant racer with a shrunken Orbinaut UCRP_RETURNMARKTOSENDER, // Hit the player responsible for Eggman Marking you with that explosion + UCRP_ALLANCIENTGEARS, // Collect all Ancient Gears in a map UCRP_TRACKHAZARD, // (Don't) get hit by a track hazard (maybe specific lap) diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index bb5205738..a5c069005 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -68,6 +68,7 @@ target_sources(SRB2SDL2 PRIVATE exp.c bail.c toxomister.cpp + ancient-gear.c ) add_subdirectory(versus) diff --git a/src/objects/ancient-gear.c b/src/objects/ancient-gear.c new file mode 100644 index 000000000..fba92e7ea --- /dev/null +++ b/src/objects/ancient-gear.c @@ -0,0 +1,237 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by Lachlan "Lach" Wright +// 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. +//----------------------------------------------------------------------------- +/// \file ancient-gear.c +/// \brief Ancient Gear object code. + +#include "../p_local.h" +#include "../k_objects.h" +#include "../s_sound.h" +#include "../k_endcam.h" +#include "../k_hud.h" +#include "../m_cond.h" + +#define DEATH_FREEZE_TIME (5*TICRATE) +#define DEATH_TIME (2*TICRATE) +#define DEATH_RISE_SPEED (3*FRACUNIT) + +#define DELTA_YAW (-ANG2) +#define DELTA_ROLL (ANG2) +static const angle_t DELTA_YAW_ACCELERATION = (-ANG1 / 2); +static const angle_t DELTA_ROLL_ACCELERATION = (ANG1 / 3); +static const fixed_t DELTA_SPRITEXSCALE = ((FRACUNIT/4 - FRACUNIT) / DEATH_TIME); +static const fixed_t DELTA_SPRITEYSCALE = ((FRACUNIT*3 - FRACUNIT) / DEATH_TIME); +static const tic_t TRANS_SHIFT_RATE = (DEATH_TIME / 10); + +static UINT16 numGears = 0; +static boolean allGearsCollected = false; +static player_t *collectingPlayer = NULL; + +static void UpdateAncientGearPart(mobj_t *part) +{ + mobj_t *gear = part->target; + fixed_t radius = FixedMul(FixedMul(part->movefactor, gear->scale), gear->spritexscale); + fixed_t xOffset = P_ReturnThrustX(NULL, part->angle - ANGLE_90, radius); + fixed_t yOffset = P_ReturnThrustY(NULL, part->angle - ANGLE_90, radius); + + P_InstaScale(part, gear->scale); + P_MoveOrigin(part, + gear->x + xOffset, + gear->y + yOffset, + gear->z + gear->height / 2 + ); + part->angle += gear->extravalue1; + part->rollangle += gear->extravalue2 * part->extravalue1; + part->spritexscale = gear->spritexscale; + part->spriteyscale = gear->spriteyscale; +} + +void Obj_AncientGearSpawn(mobj_t *gear) +{ + UINT8 i; + mobj_t *part = gear; + + numGears++; + + gear->extravalue1 = DELTA_YAW; + gear->extravalue2 = DELTA_ROLL; + + for (i = 0; i < 6; i++) + { + P_SetTarget(&part->hnext, P_SpawnMobjFromMobj(gear, 0, 0, 0, MT_ANCIENTGEAR_PART)); + P_SetTarget(&part->hnext->hprev, part); + part = part->hnext; + P_SetTarget(&part->target, gear); + + part->angle += (i & 1) * ANGLE_180; + + if (i < 2) // middle parts + { + part->angle += ANGLE_90; + part->movefactor = 10 * FRACUNIT; // horizontal offset from hitbox + part->extravalue1 = 0; // direction to roll the sprite + part->frame = (part->frame & ~FF_FRAMEMASK) | 1; + P_SetTarget(&part->tracer, gear); + part->flags2 |= MF2_LINKDRAW; + } + else // side parts + { + part->movefactor = 7 * FRACUNIT; // horizontal offset from hitbox + part->extravalue1 = (i & 1) * 2 - 1; // direction to roll the sprite + if (i > 3) // fake brightmaps + { + part->frame = (part->frame & ~FF_FRAMEMASK) | 3 | FF_FULLBRIGHT; + part->dispoffset++; + } + else + { + part->frame = (part->frame & ~FF_FRAMEMASK) | 2; + } + } + + UpdateAncientGearPart(part); + part->old_x = part->x; + part->old_y = part->y; + part->old_z = part->z; + part->old_angle = part->angle; + part->old_scale = part->scale; + } +} + +void Obj_AncientGearPartThink(mobj_t *part) +{ + if (P_MobjWasRemoved(part->target)) + { + P_RemoveMobj(part); + return; + } + UpdateAncientGearPart(part); +} + +void Obj_AncientGearRemoved(mobj_t *gear) +{ + while (!P_MobjWasRemoved(gear->hnext)) + { + P_RemoveMobj(gear->hnext); + } +} + +void Obj_AncientGearTouch(mobj_t *gear, mobj_t *toucher) +{ + P_KillMobj(gear, NULL, toucher, DMG_NORMAL); +} + +void Obj_AncientGearDeath(mobj_t *gear, mobj_t *source) +{ + if (--numGears == 0) + { + allGearsCollected = true; + M_UpdateUnlockablesAndExtraEmblems(true, true); + } + + gear->fuse = DEATH_TIME; + gear->shadowscale = gear->spritexscale; + + // give the gear some upwards momentum + gear->flags |= MF_NOCLIPHEIGHT; + P_SetObjectMomZ(gear, DEATH_RISE_SPEED, false); + + // don't activate the round win camera if there is no camera target, + // or if the round win camera is already active for any actual reason + if (P_MobjWasRemoved(source) || source->player == NULL || g_endcam.active) + { + return; + } + + // track the collecting player to display a message for them + collectingPlayer = source->player; + P_SetTarget(&gear->target, source); + + // play the collection jingle! + S_StartSound(NULL, gear->info->seesound); + + // fade out the music for as long as the sound plays + g_musicfade.start = leveltime; + g_musicfade.end = g_musicfade.start + DEATH_FREEZE_TIME; + g_musicfade.fade = 12; + g_musicfade.ticked = false; + + // start the round win camera + gear->flags2 |= MF2_BEYONDTHEGRAVE; // a gear with this flag will stop the round win camera upon next thinking + K_StartRoundWinCamera( + source, + source->player->angleturn, + FixedMul(cv_cam_dist[0].value, mapobjectscale), + 6*TICRATE, + FRACUNIT/16, + DEATH_FREEZE_TIME + ); +} + +void Obj_AncientGearDeadThink(mobj_t *gear) +{ + mobj_t *part = gear; + + // if the round win camera was activated, tell it to stop focusing on the player, + // and show the player a message + if (gear->flags2 & MF2_BEYONDTHEGRAVE) + { + gear->flags2 &= ~MF2_BEYONDTHEGRAVE; + K_StopRoundWinCamera(); + + collectingPlayer = NULL; + K_AddMessage( + numGears > 0 ? va("%d Ancient Gear%s left!", numGears, numGears == 1 ? "" : "s") + : "All Ancient Gears collected!" + , true, false + ); + } + + // play another sound once immediately after the round win camera finishes + if (!(gear->flags2 & MF2_DONTRESPAWN)) + { + gear->flags2 |= MF2_DONTRESPAWN; + S_StartSound(gear, gear->info->deathsound); + } + + // increase the translucency level every so often + if (gear->fuse % TRANS_SHIFT_RATE == 0) + { + while (!P_MobjWasRemoved(part = part->hnext)) + { + part->frame += FF_TRANS10; + } + } + + // accelerate the spinning and rotating + gear->extravalue1 += DELTA_YAW_ACCELERATION; + gear->extravalue2 += DELTA_ROLL_ACCELERATION; + + // stretch the gear + gear->spritexscale += DELTA_SPRITEXSCALE; + gear->spriteyscale += DELTA_SPRITEYSCALE; + gear->shadowscale = gear->spritexscale; +} + +void Obj_AncientGearLevelInit(void) +{ + numGears = 0; + allGearsCollected = false; + collectingPlayer = NULL; +} + +player_t *Obj_GetAncientGearCollectingPlayer(void) +{ + return collectingPlayer; +} + +boolean Obj_AllAncientGearsCollected(void) +{ + return allGearsCollected; +} diff --git a/src/p_inter.c b/src/p_inter.c index 715cc27c3..687a2edcc 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1137,6 +1137,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) Obj_ToxomisterCloudCollide(special, toucher); return; + case MT_ANCIENTGEAR: + Obj_AncientGearTouch(special, toucher); + return; + default: // SOC or script pickup P_SetTarget(&special->target, toucher); break; @@ -2364,6 +2368,9 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget case MT_FLYBOT767: Obj_FlybotDeath(target); break; + case MT_ANCIENTGEAR: + Obj_AncientGearDeath(target, source); + break; default: break; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 17f565c7c..7d520b591 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6798,6 +6798,11 @@ static void P_MobjSceneryThink(mobj_t *mobj) Obj_TickStoneShoeChain(mobj); return; } + case MT_ANCIENTGEAR_PART: + { + Obj_AncientGearPartThink(mobj); + return; + } default: if (mobj->fuse) { // Scenery object fuse! Very basic! @@ -7000,6 +7005,11 @@ static boolean P_MobjDeadThink(mobj_t *mobj) VS_BlendEye_Generator_DeadThinker(mobj); break; } + case MT_ANCIENTGEAR: + { + Obj_AncientGearDeadThink(mobj); + break; + } default: break; } @@ -11179,6 +11189,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) case MT_BATTLEUFO: case MT_SPRAYCAN: case MT_CHECKPOINT_END: + case MT_ANCIENTGEAR: thing->shadowscale = FRACUNIT; break; case MT_SMALLMACE: @@ -11724,6 +11735,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_CABOTRON: Obj_SSCabotronMobjSpawn(mobj); break; + case MT_ANCIENTGEAR: + Obj_AncientGearSpawn(mobj); + break; default: break; } @@ -12011,6 +12025,11 @@ void P_RemoveMobj(mobj_t *mobj) Obj_FlybotRemoved(mobj); break; } + case MT_ANCIENTGEAR: + { + Obj_AncientGearRemoved(mobj); + break; + } default: { break; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 80388f612..0ea682787 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -7634,6 +7634,8 @@ static void P_InitLevelSettings(void) nummapspraycans = 0; numchallengedestructibles = 0; + Obj_AncientGearLevelInit(); + // circuit, race and competition stuff numcheatchecks = 0; diff --git a/src/sounds.c b/src/sounds.c index 679247370..a2268630b 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1543,6 +1543,9 @@ sfxinfo_t S_sfx[NUMSFX] = // :apple: {"aple", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + // Ancient Gear + {"gotgea", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + // SRB2kart - Skin sounds {"kwin", false, 64, 96, -1, NULL, 0, SKSKWIN, -1, LUMPERROR, ""}, {"klose", false, 64, 96, -1, NULL, 0, SKSKLOSE, -1, LUMPERROR, ""}, diff --git a/src/sounds.h b/src/sounds.h index 90034ba72..2dd034866 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1619,6 +1619,9 @@ typedef enum // :apple: sfx_aple, + // Ancient Gear + sfx_gotgea, + // And LASTLY, Kart's skin sounds. sfx_kwin, sfx_klose,