diff --git a/extras/conf/D3R-Config.cfg b/extras/conf/D3R-Config.cfg index 0482dc439..dac4b0135 100644 --- a/extras/conf/D3R-Config.cfg +++ b/extras/conf/D3R-Config.cfg @@ -139,7 +139,7 @@ skins // Gametypes gametypes { - -1 = "Single Player"; + -1 = "Grand Prix"; 0 = "Race"; 1 = "Battle"; } @@ -5113,6 +5113,7 @@ thingtypes height = 92; } } + waypoints { color = 4; // Red @@ -5161,6 +5162,80 @@ thingtypes fixedrotation = 1; } } + + duel + { + color = 4; // Red + arrow = 0; + title = "Duel Objects"; + sprite = "SPBMA2A8"; + flags1text = "[1] Spawn in all modes"; + + 2050 + { + title = "Duel Bomb"; + width = 24; + height = 48; + arrow = 1; + flags8text = "[8] Flip strafe"; + } + + 2051 + { + title = "Banana"; + sprite = "BANAA2A8"; + width = 16; + height = 32; + } + + 2052 + { + title = "Eggman Item"; + sprite = "FITMA0"; + width = 24; + height = 32; + } + + 2053 + { + title = "Proximity Mine"; + sprite = "SSMNA0"; + width = 16; + height = 24; + } + + 2054 + { + title = "Land Mine"; + sprite = "LNDMALAR"; + width = 24; + height = 32; + } + + 2055 + { + title = "Hyudoro"; + sprite = "HYUUA2A8"; + width = 32; + height = 24; + } + + 2056 + { + title = "Drop Target"; + sprite = "DTRGALAR"; + width = 45; + height = 32; + } + + 2057 + { + title = "Pogo Spring"; + sprite = "POGSA0"; + width = 48; + height = 32; + } + } } //Default things filters @@ -5183,24 +5258,6 @@ thingsfilters } filter2 - { - name = "Enemies"; - category = "enemies"; - type = -1; - - } - - - filter3 - { - name = "NiGHTS Track"; - category = "nightstrk"; - type = -1; - - } - - - filter4 { name = "Normal Gravity"; category = ""; @@ -5213,8 +5270,7 @@ thingsfilters } - - filter5 + filter3 { name = "Reverse Gravity"; category = ""; @@ -5226,11 +5282,4 @@ thingsfilters } } - - filter6 - { - name = "Boss Waypoints"; - category = ""; - type = 292; - } } diff --git a/src/deh_tables.c b/src/deh_tables.c index eda614fb6..2025d4f03 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5372,6 +5372,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_SINK_SHIELD", "MT_SINKTRAIL", + "MT_DUELBOMB", // Duel mode bombs + "MT_BATTLEBUMPER", // Battle Mode bumper "MT_BATTLEBUMPER_DEBRIS", "MT_BATTLEBUMPER_BLAST", diff --git a/src/doomstat.h b/src/doomstat.h index dc6370ece..dc94ed0e6 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -669,6 +669,7 @@ extern boolean thwompsactive; extern UINT8 lastLowestLap; extern SINT8 spbplace; extern boolean rainbowstartavailable; +extern boolean inDuel; extern tic_t bombflashtimer; // Used to avoid causing seizures if multiple mines explode close to you :) extern boolean legitimateexit; diff --git a/src/g_game.c b/src/g_game.c index 3dfac9246..d5f134a2e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -312,6 +312,7 @@ boolean thwompsactive; // Thwomps activate on lap 2 UINT8 lastLowestLap; // Last lowest lap, for activating race lap executors SINT8 spbplace; // SPB exists, give the person behind better items boolean rainbowstartavailable; // Boolean, keeps track of if the rainbow start was gotten +boolean inDuel; // Boolean, keeps track of if it is a 1v1 // Client-sided, unsynched variables (NEVER use in anything that needs to be synced with other players) tic_t bombflashtimer = 0; // Cooldown before another FlashPal can be intialized by a bomb exploding near a displayplayer. Avoids seizures. diff --git a/src/info.c b/src/info.c index 42abd5b2e..ee71d9972 100644 --- a/src/info.c +++ b/src/info.c @@ -8315,7 +8315,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }, { // MT_POGOSPRING - -1, // doomednum + 2057, // doomednum S_POGOSPRING1, // spawnstate 1000, // spawnhealth S_POGOSPRING2B, // seestate @@ -23317,7 +23317,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }, { // MT_EGGMANITEM - -1, // doomednum + 2052, // doomednum S_EGGMANITEM1, // spawnstate 2, // spawnhealth S_NULL, // seestate @@ -23371,7 +23371,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }, { // MT_BANANA - -1, // doomednum + 2051, // doomednum S_BANANA, // spawnstate 2, // spawnhealth S_NULL, // seestate @@ -23560,7 +23560,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }, { // MT_SSMINE - -1, // doomednum + 2053, // doomednum S_SSMINE_AIR1, // spawnstate 1, // spawnhealth S_NULL, // seestate @@ -23695,7 +23695,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }, { // MT_LANDMINE - -1, // doomednum + 2054, // doomednum S_LANDMINE, // spawnstate 2, // spawnhealth S_NULL, // seestate @@ -23722,7 +23722,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }, { // MT_DROPTARGET - -1, // doomednum + 2056, // doomednum S_DROPTARGET, // spawnstate 3, // spawnhealth S_NULL, // seestate @@ -24154,7 +24154,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }, { // MT_HYUDORO_CENTER - -1, // doomednum + 2055, // doomednum S_INVISIBLE, // spawnstate 1000, // spawnhealth S_NULL, // seestate @@ -24423,6 +24423,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_DUELBOMB + 2050, // doomednum + S_SPB1, // 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 + 64*FRACUNIT, // speed + 24*FRACUNIT, // radius + 48*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags + S_NULL // raisestate + }, + { // MT_BATTLEBUMPER -1, // doomednum S_BATTLEBUMPER1,// spawnstate diff --git a/src/info.h b/src/info.h index 48850f5d0..975122946 100644 --- a/src/info.h +++ b/src/info.h @@ -6418,6 +6418,8 @@ typedef enum mobj_type MT_SINK_SHIELD, MT_SINKTRAIL, + MT_DUELBOMB, // Duel mode bombs + MT_BATTLEBUMPER, // Battle Mode bumpers MT_BATTLEBUMPER_DEBRIS, MT_BATTLEBUMPER_BLAST, diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 5b6f1ccca..48c5745fd 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -422,6 +422,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) case MT_BALLHOG: case MT_SPB: case MT_BUBBLESHIELDTRAP: + case MT_DUELBOMB: K_AddDodgeObject(thing, side, 20); break; case MT_SHRINK_GUN: diff --git a/src/k_kart.c b/src/k_kart.c index 47ffb8c9b..7625eff58 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -49,10 +49,53 @@ // battlewanted is an array of the WANTED player nums, -1 for no player in that slot // mapreset is set when enough players fill an empty server +boolean K_IsDuelItem(mobjtype_t type) +{ + switch (type) + { + case MT_DUELBOMB: + case MT_BANANA: + case MT_EGGMANITEM: + case MT_SSMINE: + case MT_LANDMINE: + case MT_HYUDORO_CENTER: + case MT_DROPTARGET: + case MT_POGOSPRING: + return true; + + default: + return false; + } +} + +boolean K_DuelItemAlwaysSpawns(mapthing_t *mt) +{ + return (mt->options & MTF_EXTRA); +} + +static void K_SpawnDuelOnlyItems(void) +{ + mapthing_t *mt = NULL; + size_t i; + + mt = mapthings; + for (i = 0; i < nummapthings; i++, mt++) + { + mobjtype_t type = P_GetMobjtype(mt->type); + + if (K_IsDuelItem(type) == true + && K_DuelItemAlwaysSpawns(mt) == false) + { + P_SpawnMapThing(mt); + } + } +} + void K_TimerReset(void) { starttime = introtime = 3; numbulbs = 1; + inDuel = false; } void K_TimerInit(void) @@ -78,6 +121,9 @@ void K_TimerInit(void) numPlayers++; } + // 1v1 activates DUEL rules! + inDuel = (numPlayers == 2); + if (numPlayers >= 2) { rainbowstartavailable = true; @@ -106,6 +152,12 @@ void K_TimerInit(void) // NOW you can try to spawn in the Battle capsules, if there's not enough players for a match K_BattleInit(); + + if (inDuel == true) + { + K_SpawnDuelOnlyItems(); + } + //CONS_Printf("numbulbs set to %d (%d players, %d spectators) on tic %d\n", numbulbs, numPlayers, numspec, leveltime); } @@ -1437,6 +1489,7 @@ fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) break; case MT_ORBINAUT: case MT_ORBINAUT_SHIELD: + case MT_DUELBOMB: if (against->player) weight = K_PlayerWeight(against, NULL); break; diff --git a/src/k_kart.h b/src/k_kart.h index 7973a236c..a5d2b0185 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -38,6 +38,9 @@ angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t void K_RegisterKartStuff(void); +boolean K_IsDuelItem(mobjtype_t type); +boolean K_DuelItemAlwaysSpawns(mapthing_t *mt); + void K_TimerReset(void); void K_TimerInit(void); UINT32 K_GetPlayerDontDrawFlag(player_t *player); diff --git a/src/k_objects.h b/src/k_objects.h index b5b58b800..96e0fa2b5 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -3,6 +3,7 @@ #define k_objects_H /* Hyudoro */ +void Obj_InitHyudoroCenter(mobj_t *center, mobj_t *master); void Obj_HyudoroDeploy(mobj_t *master); void Obj_HyudoroThink(mobj_t *actor); void Obj_HyudoroCenterThink(mobj_t *actor); @@ -47,4 +48,10 @@ void Obj_OrbinautJawzMoveHeld(player_t *player); void Obj_JawzThink(mobj_t *th); void Obj_JawzThrown(mobj_t *th, fixed_t finalSpeed, SINT8 dir); +/* Duel Bomb */ +void Obj_DuelBombThink(mobj_t *bomb); +void Obj_DuelBombReverse(mobj_t *bomb); +void Obj_DuelBombTouch(mobj_t *bomb, mobj_t *toucher); +void Obj_DuelBombInit(mobj_t *bomb); + #endif/*k_objects_H*/ diff --git a/src/objects/Sourcefile b/src/objects/Sourcefile index 044c9b576..b8cb63b1f 100644 --- a/src/objects/Sourcefile +++ b/src/objects/Sourcefile @@ -6,3 +6,4 @@ spb.c manta-ring.c orbinaut.c jawz.c +duel-bomb.c diff --git a/src/objects/duel-bomb.c b/src/objects/duel-bomb.c new file mode 100644 index 000000000..173efb933 --- /dev/null +++ b/src/objects/duel-bomb.c @@ -0,0 +1,97 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2022 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 duel-bomb.c +/// \brief Duel mode bombs. + +#include "../doomdef.h" +#include "../doomstat.h" +#include "../info.h" +#include "../k_kart.h" +#include "../k_objects.h" +#include "../m_random.h" +#include "../p_local.h" +#include "../r_main.h" +#include "../s_sound.h" +#include "../g_game.h" +#include "../z_zone.h" +#include "../k_waypoint.h" +#include "../k_respawn.h" +#include "../k_collide.h" + +#define bomb_dir(o) ((o)->movedir) + +static fixed_t GetBombSpeed(mobj_t *bomb) +{ + return FixedMul(bomb->info->speed, bomb->scale); +} + +static void UpdateBombMovement(mobj_t *bomb) +{ + const fixed_t spd = GetBombSpeed(bomb); + bomb->momx = FixedMul(spd, FINECOSINE(bomb_dir(bomb) >> ANGLETOFINESHIFT)); + bomb->momy = FixedMul(spd, FINESINE(bomb_dir(bomb) >> ANGLETOFINESHIFT)); +} + +void Obj_DuelBombThink(mobj_t *bomb) +{ + boolean grounded = P_IsObjectOnGround(bomb); + + if (grounded == true) + { + UpdateBombMovement(bomb); + } +} + +void Obj_DuelBombReverse(mobj_t *bomb) +{ + bomb_dir(bomb) += ANGLE_180; + UpdateBombMovement(bomb); +} + +void Obj_DuelBombTouch(mobj_t *bomb, mobj_t *toucher) +{ + player_t *player = toucher->player; + mobj_t *boom = NULL; + + if (bomb->health <= 0 || toucher->health <= 0) + { + return; + } + + if (player->flashing > 0 || player->hyudorotimer > 0 || P_PlayerInPain(player)) + { + // No interaction + return; + } + + // Create explosion + boom = P_SpawnMobjFromMobj(bomb, 0, 0, 0, MT_BOOMEXPLODE); + boom->momz = 5 * boom->scale; + boom->color = SKINCOLOR_KETCHUP; + S_StartSound(boom, bomb->info->attacksound); + + if (player->invincibilitytimer > 0 + || K_IsBigger(toucher, bomb) == true + || player->flamedash > 0) + { + // Kill without damaging. + P_KillMobj(bomb, toucher, toucher, DMG_NORMAL); + return; + } + + P_DamageMobj(toucher, bomb, bomb, 1, DMG_TUMBLE); + P_KillMobj(bomb, toucher, toucher, DMG_NORMAL); +} + +void Obj_DuelBombInit(mobj_t *bomb) +{ + bomb_dir(bomb) = bomb->angle + ANGLE_90; + UpdateBombMovement(bomb); +} diff --git a/src/objects/hyudoro.c b/src/objects/hyudoro.c index 53b7ddc91..2b6d8cf1a 100644 --- a/src/objects/hyudoro.c +++ b/src/objects/hyudoro.c @@ -1,3 +1,15 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2022 by James R. +// Copyright (C) 2022 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 hyudoro.c +/// \brief Hyudoro item code. + #include "../doomdef.h" #include "../doomstat.h" #include "../info.h" @@ -7,6 +19,7 @@ #include "../p_local.h" #include "../r_main.h" #include "../s_sound.h" +#include "../g_game.h" enum { HYU_PATROL, @@ -39,11 +52,14 @@ K_ChangePlayerItem #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_center_max_radius(o) ((o)->threshold) #define hyudoro_center_master(o) ((o)->target) @@ -101,6 +117,16 @@ sine_bob sineofs + FINESINE(a >> ANGLETOFINESHIFT)); } +static void +bob_in_place +( mobj_t * hyu, + INT32 bob_speed) +{ + sine_bob(hyu, + (leveltime & (bob_speed - 1)) * + (ANGLE_MAX / bob_speed), -(3*FRACUNIT/4)); +} + static void project_hyudoro (mobj_t *hyu) { @@ -127,8 +153,6 @@ project_hyudoro (mobj_t *hyu) static void project_hyudoro_hover (mobj_t *hyu) { - const INT32 bob_speed = 64; - mobj_t *target = hyudoro_target(hyu); // Turns a bit toward its target @@ -154,9 +178,7 @@ project_hyudoro_hover (mobj_t *hyu) hyu->pitch = target->pitch; hyu->roll = target->roll; - sine_bob(hyu, - (leveltime & (bob_speed - 1)) * - (ANGLE_MAX / bob_speed), -(3*FRACUNIT/4)); + bob_in_place(hyu, 64); } static void @@ -173,6 +195,79 @@ spawn_hyudoro_shadow (mobj_t *hyu) P_SetTarget(&shadow->tracer, hyu); } +static mobj_t * +find_duel_target (mobj_t *ignore) +{ + mobj_t *ret = NULL; + UINT8 bestPosition = UINT8_MAX; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *player = NULL; + + if (playeringame[i] == false) + { + continue; + } + + player = &players[i]; + if (player->spectator || player->exiting) + { + continue; + } + + if (!player->mo || P_MobjWasRemoved(player->mo)) + { + continue; + } + + if (ignore != NULL && player->mo == ignore) + { + continue; + } + + if (player->position < bestPosition) + { + ret = player->mo; + bestPosition = player->position; + + if (bestPosition <= 1) + { + // Can't get any lower + break; + } + } + } + + return ret; +} + +static void +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))); + + // Spin in circles + hyu->angle += ANGLE_45; + + // Bob very fast + bob_in_place(hyu, 32); + + hyu->sprzoff += hyu->height; +} + static void move_to_player (mobj_t *hyu) { @@ -181,8 +276,11 @@ move_to_player (mobj_t *hyu) angle_t angle; fixed_t speed; - if (!target) + if (!target || P_MobjWasRemoved(target)) + { + do_confused(hyu); return; + } angle = R_PointToAngle2( hyu->x, hyu->y, target->x, target->y); @@ -232,6 +330,9 @@ deliver_item (mobj_t *hyu) hyu->destscale = target->scale / 4; hyu->scalespeed = abs(hyu->scale - hyu->destscale) / hyu->tics; + + // sets as already delivered + hyudoro_itemtype(hyu) = KITEM_NONE; } static void @@ -287,11 +388,14 @@ hyudoro_patrol_hit_player mobj_t *center = hyudoro_center(hyu); + mobj_t *master = NULL; + if (!player) return false; // Cannot hit its master - if (toucher == get_hyudoro_master(hyu)) + master = get_hyudoro_master(hyu); + if (toucher == master) return false; // Don't punish a punished player @@ -313,8 +417,15 @@ hyudoro_patrol_hit_player player->hyudorotimer = hyudorotime; player->stealingtimer = hyudorotime; - P_SetTarget(&hyudoro_target(hyu), - hyudoro_center_master(center)); + P_SetTarget(&hyudoro_stolefrom(hyu), toucher); + + if (master == NULL || P_MobjWasRemoved(master)) + { + // if master is NULL, it is probably a DUEL + master = find_duel_target(toucher); + } + + P_SetTarget(&hyudoro_target(hyu), master); if (center) P_RemoveMobj(center); @@ -388,11 +499,8 @@ hyudoro_hover_await_stack (mobj_t *hyu) } void -Obj_HyudoroDeploy (mobj_t *master) +Obj_InitHyudoroCenter (mobj_t * center, mobj_t * master) { - mobj_t *center = P_SpawnMobjFromMobj( - master, 0, 0, 0, MT_HYUDORO_CENTER); - mobj_t *hyu = P_SpawnMobjFromMobj( center, 0, 0, 0, MT_HYUDORO); @@ -405,20 +513,30 @@ Obj_HyudoroDeploy (mobj_t *master) center->radius = hyu->radius; - hyu->angle = master->angle; + hyu->angle = center->angle; P_SetTarget(&hyudoro_center(hyu), center); P_SetTarget(&hyudoro_center_master(center), master); hyudoro_mode(hyu) = HYU_PATROL; // Set splitscreen player visibility - if (master->player) + hyu->renderflags |= RF_DONTDRAW; + if (master && !P_MobjWasRemoved(master) && master->player) { - hyu->renderflags |= RF_DONTDRAW & - ~(K_GetPlayerDontDrawFlag(master->player)); + hyu->renderflags &= ~(K_GetPlayerDontDrawFlag(master->player)); } spawn_hyudoro_shadow(hyu); // this sucks btw +} + +void +Obj_HyudoroDeploy (mobj_t *master) +{ + mobj_t *center = P_SpawnMobjFromMobj( + master, 0, 0, 0, MT_HYUDORO_CENTER); + + center->angle = master->angle; + Obj_InitHyudoroCenter(center, master); S_StartSound(master, sfx_s3k92); // scary ghost noise } diff --git a/src/p_inter.c b/src/p_inter.c index 32214e036..b7e26d63a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -356,6 +356,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) Obj_SPBTouch(special, toucher); return; } + case MT_DUELBOMB: + { + Obj_DuelBombTouch(special, toucher); + return; + } case MT_EMERALD: if (!P_CanPickupItem(player, 0)) return; @@ -1012,7 +1017,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget { target->fuse = 2; } - else + else if (inDuel == false) { UINT8 i; diff --git a/src/p_mobj.c b/src/p_mobj.c index 20164ea9d..6df9b921c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1707,7 +1707,13 @@ void P_XYMovement(mobj_t *mo) if (walltransferred == false) { - if (mo->flags & MF_SLIDEME) + if (mo->type == MT_DUELBOMB) + { + P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_BUMP); + Obj_DuelBombReverse(mo); + xmove = ymove = 0; + } + else if (mo->flags & MF_SLIDEME) { P_SlideMove(mo); if (P_MobjWasRemoved(mo)) @@ -1759,13 +1765,13 @@ void P_XYMovement(mobj_t *mo) } break; + case MT_BUBBLESHIELDTRAP: + S_StartSound(mo, sfx_s3k44); // Bubble bounce + break; + default: break; } - - // Bubble bounce - if (mo->type == MT_BUBBLESHIELDTRAP) - S_StartSound(mo, sfx_s3k44); } } } @@ -5132,6 +5138,7 @@ boolean P_IsKartFieldItem(INT32 type) case MT_POGOSPRING: case MT_SINK: case MT_DROPTARGET: + case MT_DUELBOMB: return true; default: @@ -7008,6 +7015,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_LANDMINE: mobj->friction = ORIG_FRICTION/4; + if (mobj->target && mobj->target->player) + mobj->color = mobj->target->player->skincolor; + else + mobj->color = SKINCOLOR_SAPPHIRE; + if (mobj->momx || mobj->momy || mobj->momz) { mobj_t *ghost = P_SpawnGhostMobj(mobj); @@ -7066,6 +7078,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_SPBEXPLOSION: mobj->health--; break; + case MT_DUELBOMB: + { + Obj_DuelBombThink(mobj); + break; + } case MT_EMERALD: { if (battleovertime.enabled >= 10*TICRATE) @@ -9890,6 +9907,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) case MT_SINK: case MT_ROCKETSNEAKER: case MT_SPB: + case MT_DUELBOMB: thing->shadowscale = 3*FRACUNIT/2; break; case MT_BANANA_SHIELD: @@ -11741,10 +11759,22 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) break; } + if (inDuel == false) + { + if (K_IsDuelItem(i) == true + && K_DuelItemAlwaysSpawns(mthing) == false) + { + // Only spawns in Duels. + return false; + } + } + // No bosses outside of a combat situation. // (just in case we want boss arenas to do double duty as battle maps) if (!bossinfo.boss && (mobjinfo[i].flags & MF_BOSS)) + { return false; + } if (metalrecording) // Metal Sonic can't use these things. { @@ -12903,6 +12933,33 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean // Increment no. of capsules on the map counter maptargets++; + break; + } + case MT_DUELBOMB: + { + // Duel Bomb needs init to match real map thing's angle + mobj->angle = FixedAngle(mthing->angle << FRACBITS); + Obj_DuelBombInit(mobj); + *doangle = false; + break; + } + case MT_BANANA: + { + // Give Duel bananas a random angle + mobj->angle = FixedMul(P_RandomFixed(PR_DECORATION), ANGLE_MAX); + *doangle = false; + break; + } + case MT_HYUDORO_CENTER: + { + Obj_InitHyudoroCenter(mobj, NULL); + break; + } + case MT_POGOSPRING: + { + // Start as tumble version. + mobj->reactiontime++; + break; } default: break; @@ -12943,6 +13000,11 @@ static void P_SetAmbush(mobj_t *mobj) mobj->type != MT_NIGHTSBUMPER && mobj->type != MT_STARPOST) mobj->flags2 |= MF2_AMBUSH; + + if (mobj->type == MT_DUELBOMB) + { + Obj_DuelBombReverse(mobj); + } } static void P_SetObjectSpecial(mobj_t *mobj) diff --git a/src/p_saveg.c b/src/p_saveg.c index 3ae89befb..62f11e7ef 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4591,6 +4591,7 @@ static void P_NetArchiveMisc(boolean resending) WRITEUINT8(save_p, lastLowestLap); WRITESINT8(save_p, spbplace); WRITEUINT8(save_p, rainbowstartavailable); + WRITEUINT8(save_p, inDuel); WRITEUINT32(save_p, introtime); WRITEUINT32(save_p, starttime); @@ -4750,6 +4751,7 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) lastLowestLap = READUINT8(save_p); spbplace = READSINT8(save_p); rainbowstartavailable = (boolean)READUINT8(save_p); + inDuel = (boolean)READUINT8(save_p); introtime = READUINT32(save_p); starttime = READUINT32(save_p);