Merge branch 'top-final'

This commit is contained in:
James R 2022-09-29 10:51:59 -07:00
commit f3668fbd7c
23 changed files with 1288 additions and 118 deletions

View file

@ -362,7 +362,6 @@ consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS] = { //Alam: Dummy for save
#endif
// SRB2kart
consvar_t cv_superring = CVAR_INIT ("superring", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_sneaker = CVAR_INIT ("sneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_rocketsneaker = CVAR_INIT ("rocketsneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_invincibility = CVAR_INIT ("invincibility", "On", CV_NETVAR, CV_OnOff, NULL);
@ -372,7 +371,6 @@ consvar_t cv_orbinaut = CVAR_INIT ("orbinaut", "On", CV_NETVAR, CV_OnOff,
consvar_t cv_jawz = CVAR_INIT ("jawz", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_mine = CVAR_INIT ("mine", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_landmine = CVAR_INIT ("landmine", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_droptarget = CVAR_INIT ("droptarget", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_ballhog = CVAR_INIT ("ballhog", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_selfpropelledbomb = CVAR_INIT ("selfpropelledbomb", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_grow = CVAR_INIT ("grow", "On", CV_NETVAR, CV_OnOff, NULL);
@ -382,7 +380,10 @@ consvar_t cv_bubbleshield = CVAR_INIT ("bubbleshield", "On", CV_NETVAR, CV_O
consvar_t cv_flameshield = CVAR_INIT ("flameshield", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_hyudoro = CVAR_INIT ("hyudoro", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_pogospring = CVAR_INIT ("pogospring", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_superring = CVAR_INIT ("superring", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_kitchensink = CVAR_INIT ("kitchensink", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_droptarget = CVAR_INIT ("droptarget", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_gardentop = CVAR_INIT ("gardentop", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_dualsneaker = CVAR_INIT ("dualsneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_triplesneaker = CVAR_INIT ("triplesneaker", "On", CV_NETVAR, CV_OnOff, NULL);

View file

@ -72,14 +72,38 @@ extern consvar_t cv_pause;
extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_maxplayers, cv_respawntime;
// SRB2kart items
extern consvar_t cv_superring, cv_sneaker, cv_rocketsneaker, cv_invincibility, cv_banana;
extern consvar_t cv_eggmanmonitor, cv_orbinaut, cv_jawz, cv_mine, cv_landmine, cv_droptarget;
extern consvar_t cv_ballhog, cv_selfpropelledbomb, cv_grow, cv_shrink;
extern consvar_t cv_lightningshield, cv_bubbleshield, cv_flameshield;
extern consvar_t cv_hyudoro, cv_pogospring, cv_kitchensink;
extern consvar_t
cv_sneaker,
cv_rocketsneaker,
cv_invincibility,
cv_banana,
cv_eggmanmonitor,
cv_orbinaut,
cv_jawz,
cv_mine,
cv_landmine,
cv_ballhog,
cv_selfpropelledbomb,
cv_grow,
cv_shrink,
cv_lightningshield,
cv_bubbleshield,
cv_flameshield,
cv_hyudoro,
cv_pogospring,
cv_superring,
cv_kitchensink,
cv_droptarget,
cv_gardentop;
extern consvar_t cv_dualsneaker, cv_triplesneaker, cv_triplebanana, cv_decabanana;
extern consvar_t cv_tripleorbinaut, cv_quadorbinaut, cv_dualjawz;
extern consvar_t
cv_dualsneaker,
cv_triplesneaker,
cv_triplebanana,
cv_decabanana,
cv_tripleorbinaut,
cv_quadorbinaut,
cv_dualjawz;
extern consvar_t cv_kartminimap;
extern consvar_t cv_kartcheck;

View file

@ -159,7 +159,8 @@ Run this macro, then #undef FOREACH afterward
FOREACH (POGOSPRING, 18),\
FOREACH (SUPERRING, 19),\
FOREACH (KITCHENSINK, 20),\
FOREACH (DROPTARGET, 21)
FOREACH (DROPTARGET, 21),\
FOREACH (GARDENTOP, 22)
typedef enum
{
@ -187,6 +188,7 @@ typedef enum
KSHIELD_LIGHTNING = 1,
KSHIELD_BUBBLE = 2,
KSHIELD_FLAME = 3,
KSHIELD_TOP = 4,
NUMKARTSHIELDS
} kartshields_t;
@ -288,6 +290,8 @@ typedef enum
#define ITEMSCALE_GROW 1
#define ITEMSCALE_SHRINK 2
#define GARDENTOP_MAXGRINDTIME (45)
// player_t struct for all respawn variables
typedef struct respawnvars_s
{
@ -598,6 +602,8 @@ typedef struct player_s
UINT8 kickstartaccel;
UINT8 stairjank;
UINT8 topdriftheld;
UINT8 topinfirst;
UINT8 shrinkLaserDelay;

View file

@ -3740,6 +3740,14 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_FLAMESHIELDLINE3",
"S_FLAMESHIELDFLASH",
// Marble Garden Zone Spinning Top
"S_GARDENTOP_FLOATING",
"S_GARDENTOP_SINKING1",
"S_GARDENTOP_SINKING2",
"S_GARDENTOP_SINKING3",
"S_GARDENTOP_DEAD",
"S_GARDENTOPSPARK",
// Caked-Up Booty-Sheet Ghost
"S_HYUDORO",
@ -5346,6 +5354,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_FLAMESHIELDUNDERLAY",
"MT_FLAMESHIELDPAPER",
"MT_BUBBLESHIELDTRAP",
"MT_GARDENTOP",
"MT_GARDENTOPSPARK",
"MT_HYUDORO",
"MT_HYUDORO_CENTER",
@ -6668,6 +6678,7 @@ struct int_const_s const INT_CONST[] = {
{"KSHIELD_LIGHTNING",KSHIELD_LIGHTNING},
{"KSHIELD_BUBBLE",KSHIELD_BUBBLE},
{"KSHIELD_FLAME",KSHIELD_FLAME},
{"KSHIELD_TOP",KSHIELD_TOP},
{"NUMKARTSHIELDS",NUMKARTSHIELDS},
// kartspinoutflags_t

View file

@ -4320,6 +4320,13 @@ state_t states[NUMSTATES] =
{SPR_FLML, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE|14, 7, {NULL}, 6, 1, S_NULL}, // S_FLAMESHIELDLINE3
{SPR_FLMF, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_FLAMESHIELDFLASH
{SPR_GTOP, FF_ANIMATE, -1, {NULL}, 5, 1, S_NULL}, // S_GARDENTOP_FLOATING
{SPR_GTOP, 0, 1, {NULL}, 5, 1, S_GARDENTOP_SINKING2}, // S_GARDENTOP_SINKING1
{SPR_GTOP, 2, 1, {NULL}, 5, 1, S_GARDENTOP_SINKING3}, // S_GARDENTOP_SINKING2
{SPR_GTOP, 4, 1, {NULL}, 5, 1, S_GARDENTOP_SINKING1}, // S_GARDENTOP_SINKING3
{SPR_GTOP, FF_ANIMATE, 100, {A_Scream}, 5, 1, S_NULL}, // S_GARDENTOP_DEAD
{SPR_BDRF, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 5, 2, S_NULL}, // S_GARDENTOPSPARK
{SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO
{SPR_GRWP, FF_FULLBRIGHT|FF_ANIMATE, 13, {NULL}, 7, 1, S_NULL}, // S_GROW_PARTICLE
@ -24065,6 +24072,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_GARDENTOP
-1, // doomednum
S_GARDENTOP_FLOATING, // spawnstate
8, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
4, // reactiontime
sfx_s3k8b, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_GARDENTOP_DEAD, // deathstate
S_NULL, // xdeathstate
sfx_s3k7a, // deathsound
40*FRACUNIT, // speed
30*FRACUNIT, // radius
68*FRACUNIT, // height
-1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_GARDENTOPSPARK
-1, // doomednum
S_GARDENTOPSPARK, // 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
8, // speed
8*FRACUNIT, // radius
8*FRACUNIT, // height
1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_NOSQUISH, // flags
S_NULL // raisestate
},
{ // MT_HYUDORO
-1, // doomednum
S_HYUDORO, // spawnstate

View file

@ -4750,6 +4750,14 @@ typedef enum state
S_FLAMESHIELDLINE3,
S_FLAMESHIELDFLASH,
// Marble Garden Zone Spinning Top
S_GARDENTOP_FLOATING,
S_GARDENTOP_SINKING1,
S_GARDENTOP_SINKING2,
S_GARDENTOP_SINKING3,
S_GARDENTOP_DEAD,
S_GARDENTOPSPARK,
// Caked-Up Booty-Sheet Ghost
S_HYUDORO,
@ -6392,6 +6400,8 @@ typedef enum mobj_type
MT_FLAMESHIELDUNDERLAY,
MT_FLAMESHIELDPAPER,
MT_BUBBLESHIELDTRAP,
MT_GARDENTOP,
MT_GARDENTOPSPARK,
MT_HYUDORO,
MT_HYUDORO_CENTER,

View file

@ -66,17 +66,22 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
if (t1->type == MT_BANANA && t1->health > 1)
S_StartSound(t2, sfx_bsnipe);
damageitem = true;
if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD)
{
// Melt item
S_StartSound(t2, sfx_s3k43);
}
else if (K_IsRidingFloatingTop(t2->player))
{
// Float over silly banana
damageitem = false;
}
else
{
P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL|DMG_WOMBO);
}
damageitem = true;
}
else if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD
|| t2->type == MT_ORBINAUT || t2->type == MT_ORBINAUT_SHIELD
@ -774,11 +779,13 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
// Clash instead of damage if both parties have any of these conditions
t1Condition = (K_IsBigger(t1, t2) == true)
|| (t1->player->invincibilitytimer > 0)
|| (t1->player->flamedash > 0 && t1->player->itemtype == KITEM_FLAMESHIELD);
|| (t1->player->flamedash > 0 && t1->player->itemtype == KITEM_FLAMESHIELD)
|| (t1->player->curshield == KSHIELD_TOP && !K_IsHoldingDownTop(t1->player));
t2Condition = (K_IsBigger(t2, t1) == true)
|| (t2->player->invincibilitytimer > 0)
|| (t2->player->flamedash > 0 && t2->player->itemtype == KITEM_FLAMESHIELD);
|| (t2->player->flamedash > 0 && t2->player->itemtype == KITEM_FLAMESHIELD)
|| (t2->player->curshield == KSHIELD_TOP && !K_IsHoldingDownTop(t2->player));
if (t1Condition == true && t2Condition == true)
{

View file

@ -111,7 +111,7 @@ static patch_t *kp_itemtimer[2];
static patch_t *kp_itemmulsticker[2];
static patch_t *kp_itemx;
static patch_t *kp_superring[2];
static patch_t *kp_sadface[2];
static patch_t *kp_sneaker[2];
static patch_t *kp_rocketsneaker[2];
static patch_t *kp_invincibility[13];
@ -121,7 +121,6 @@ static patch_t *kp_orbinaut[5];
static patch_t *kp_jawz[2];
static patch_t *kp_mine[2];
static patch_t *kp_landmine[2];
static patch_t *kp_droptarget[2];
static patch_t *kp_ballhog[2];
static patch_t *kp_selfpropelledbomb[2];
static patch_t *kp_grow[2];
@ -131,8 +130,10 @@ static patch_t *kp_bubbleshield[2];
static patch_t *kp_flameshield[2];
static patch_t *kp_hyudoro[2];
static patch_t *kp_pogospring[2];
static patch_t *kp_superring[2];
static patch_t *kp_kitchensink[2];
static patch_t *kp_sadface[2];
static patch_t *kp_droptarget[2];
static patch_t *kp_gardentop[2];
static patch_t *kp_check[6];
@ -390,7 +391,7 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_itemmulsticker[0], "K_ITMUL");
HU_UpdatePatch(&kp_itemx, "K_ITX");
HU_UpdatePatch(&kp_superring[0], "K_ITRING");
HU_UpdatePatch(&kp_sadface[0], "K_ITSAD");
HU_UpdatePatch(&kp_sneaker[0], "K_ITSHOE");
HU_UpdatePatch(&kp_rocketsneaker[0], "K_ITRSHE");
@ -411,7 +412,6 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_jawz[0], "K_ITJAWZ");
HU_UpdatePatch(&kp_mine[0], "K_ITMINE");
HU_UpdatePatch(&kp_landmine[0], "K_ITLNDM");
HU_UpdatePatch(&kp_droptarget[0], "K_ITDTRG");
HU_UpdatePatch(&kp_ballhog[0], "K_ITBHOG");
HU_UpdatePatch(&kp_selfpropelledbomb[0], "K_ITSPB");
HU_UpdatePatch(&kp_grow[0], "K_ITGROW");
@ -421,8 +421,10 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_flameshield[0], "K_ITFLMS");
HU_UpdatePatch(&kp_hyudoro[0], "K_ITHYUD");
HU_UpdatePatch(&kp_pogospring[0], "K_ITPOGO");
HU_UpdatePatch(&kp_superring[0], "K_ITRING");
HU_UpdatePatch(&kp_kitchensink[0], "K_ITSINK");
HU_UpdatePatch(&kp_sadface[0], "K_ITSAD");
HU_UpdatePatch(&kp_droptarget[0], "K_ITDTRG");
HU_UpdatePatch(&kp_gardentop[0], "K_ITGTOP");
sprintf(buffer, "FSMFGxxx");
for (i = 0; i < 104; i++)
@ -447,7 +449,7 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_itemtimer[1], "K_ISIMER");
HU_UpdatePatch(&kp_itemmulsticker[1], "K_ISMUL");
HU_UpdatePatch(&kp_superring[1], "K_ISRING");
HU_UpdatePatch(&kp_sadface[1], "K_ISSAD");
HU_UpdatePatch(&kp_sneaker[1], "K_ISSHOE");
HU_UpdatePatch(&kp_rocketsneaker[1], "K_ISRSHE");
sprintf(buffer, "K_ISINVx");
@ -462,7 +464,6 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_jawz[1], "K_ISJAWZ");
HU_UpdatePatch(&kp_mine[1], "K_ISMINE");
HU_UpdatePatch(&kp_landmine[1], "K_ISLNDM");
HU_UpdatePatch(&kp_droptarget[1], "K_ISDTRG");
HU_UpdatePatch(&kp_ballhog[1], "K_ISBHOG");
HU_UpdatePatch(&kp_selfpropelledbomb[1], "K_ISSPB");
HU_UpdatePatch(&kp_grow[1], "K_ISGROW");
@ -472,8 +473,10 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_flameshield[1], "K_ISFLMS");
HU_UpdatePatch(&kp_hyudoro[1], "K_ISHYUD");
HU_UpdatePatch(&kp_pogospring[1], "K_ISPOGO");
HU_UpdatePatch(&kp_superring[1], "K_ISRING");
HU_UpdatePatch(&kp_kitchensink[1], "K_ISSINK");
HU_UpdatePatch(&kp_sadface[1], "K_ISSAD");
HU_UpdatePatch(&kp_droptarget[1], "K_ISDTRG");
HU_UpdatePatch(&kp_gardentop[1], "K_ISGTOP");
sprintf(buffer, "FSMFSxxx");
for (i = 0; i < 104; i++)
@ -662,8 +665,6 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny)
return (tiny ? "K_ISMINE" : "K_ITMINE");
case KITEM_LANDMINE:
return (tiny ? "K_ISLNDM" : "K_ITLNDM");
case KITEM_DROPTARGET:
return (tiny ? "K_ISDTRG" : "K_ITDTRG");
case KITEM_BALLHOG:
return (tiny ? "K_ISBHOG" : "K_ITBHOG");
case KITEM_SPB:
@ -686,6 +687,10 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny)
return (tiny ? "K_ISRING" : "K_ITRING");
case KITEM_KITCHENSINK:
return (tiny ? "K_ISSINK" : "K_ITSINK");
case KITEM_DROPTARGET:
return (tiny ? "K_ISDTRG" : "K_ITDTRG");
case KITEM_GARDENTOP:
return (tiny ? "K_ISGTOP" : "K_ITGTOP");
case KRITEM_TRIPLEORBINAUT:
return (tiny ? "K_ISORBN" : "K_ITORB3");
case KRITEM_QUADORBINAUT:
@ -721,6 +726,7 @@ static patch_t *K_GetCachedItemPatch(INT32 item, UINT8 offset)
kp_superring,
kp_kitchensink,
kp_droptarget,
kp_gardentop,
};
if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS))
@ -4456,6 +4462,7 @@ static void K_drawDistributionDebugger(void)
kp_superring[1],
kp_kitchensink[1],
kp_droptarget[1],
kp_gardentop[1],
kp_sneaker[1],
kp_sneaker[1],

View file

@ -218,7 +218,6 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_jawz);
CV_RegisterVar(&cv_mine);
CV_RegisterVar(&cv_landmine);
CV_RegisterVar(&cv_droptarget);
CV_RegisterVar(&cv_ballhog);
CV_RegisterVar(&cv_selfpropelledbomb);
CV_RegisterVar(&cv_grow);
@ -230,6 +229,8 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_pogospring);
CV_RegisterVar(&cv_superring);
CV_RegisterVar(&cv_kitchensink);
CV_RegisterVar(&cv_droptarget);
CV_RegisterVar(&cv_gardentop);
CV_RegisterVar(&cv_dualsneaker);
CV_RegisterVar(&cv_triplesneaker);
@ -336,6 +337,7 @@ consvar_t *KartItemCVars[NUMKARTRESULTS-1] =
&cv_superring,
&cv_kitchensink,
&cv_droptarget,
&cv_gardentop,
&cv_dualsneaker,
&cv_triplesneaker,
&cv_triplebanana,
@ -350,68 +352,70 @@ consvar_t *KartItemCVars[NUMKARTRESULTS-1] =
// Less ugly 2D arrays
static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
{
//P-Odds 0 1 2 3 4 5 6 7
/*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker
/*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker
/*Invincibility*/ { 0, 0, 0, 0, 3, 4, 5, 7 }, // Invincibility
/*Banana*/ { 2, 3, 1, 0, 0, 0, 0, 0 }, // Banana
/*Eggman Monitor*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor
/*Orbinaut*/ { 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut
/*Jawz*/ { 0, 4, 2, 1, 0, 0, 0, 0 }, // Jawz
/*Mine*/ { 0, 3, 3, 1, 0, 0, 0, 0 }, // Mine
/*Land Mine*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Land Mine
/*Ballhog*/ { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog
/*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Self-Propelled Bomb
/*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow
/*Shrink*/ { 0, 0, 0, 0, 0, 1, 3, 2 }, // Shrink
/*Lightning Shield*/ { 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield
/*Bubble Shield*/ { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield
/*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield
/*Hyudoro*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Hyudoro
/*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring
/*Super Ring*/ { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring
/*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
/*Drop Target*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target
/*Sneaker x2*/ { 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2
/*Sneaker x3*/ { 0, 0, 0, 1, 6, 9, 5, 0 }, // Sneaker x3
/*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3
/*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10
/*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3
/*Orbinaut x4*/ { 0, 0, 0, 2, 0, 0, 0, 0 }, // Orbinaut x4
/*Jawz x2*/ { 0, 0, 1, 2, 1, 0, 0, 0 } // Jawz x2
//B C D E F G H I
{ 0, 0, 2, 3, 4, 0, 0, 0 }, // Sneaker
{ 0, 0, 0, 0, 0, 3, 4, 5 }, // Rocket Sneaker
{ 0, 0, 0, 0, 2, 5, 5, 7 }, // Invincibility
{ 2, 3, 1, 0, 0, 0, 0, 0 }, // Banana
{ 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor
{ 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut
{ 0, 4, 2, 1, 0, 0, 0, 0 }, // Jawz
{ 0, 3, 3, 1, 0, 0, 0, 0 }, // Mine
{ 3, 0, 0, 0, 0, 0, 0, 0 }, // Land Mine
{ 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog
{ 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb
{ 0, 0, 0, 0, 2, 5, 0, 0 }, // Grow
{ 0, 0, 0, 0, 0, 2, 4, 2 }, // Shrink
{ 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield
{ 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield
{ 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield
{ 3, 0, 0, 0, 0, 0, 0, 0 }, // Hyudoro
{ 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring
{ 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring
{ 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
{ 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target
{ 0, 0, 0, 3, 5, 0, 0, 0 }, // Garden Top
{ 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2
{ 0, 0, 0, 0, 4, 4, 4, 0 }, // Sneaker x3
{ 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3
{ 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10
{ 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3
{ 0, 0, 0, 2, 0, 0, 0, 0 }, // Orbinaut x4
{ 0, 0, 1, 2, 1, 0, 0, 0 } // Jawz x2
};
static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
{
//P-Odds 0 1
/*Sneaker*/ { 2, 1 }, // Sneaker
/*Rocket Sneaker*/ { 0, 0 }, // Rocket Sneaker
/*Invincibility*/ { 4, 1 }, // Invincibility
/*Banana*/ { 0, 0 }, // Banana
/*Eggman Monitor*/ { 1, 0 }, // Eggman Monitor
/*Orbinaut*/ { 8, 0 }, // Orbinaut
/*Jawz*/ { 8, 1 }, // Jawz
/*Mine*/ { 6, 1 }, // Mine
/*Land Mine*/ { 2, 0 }, // Land Mine
/*Ballhog*/ { 2, 1 }, // Ballhog
/*Self-Propelled Bomb*/ { 0, 0 }, // Self-Propelled Bomb
/*Grow*/ { 2, 1 }, // Grow
/*Shrink*/ { 0, 0 }, // Shrink
/*Lightning Shield*/ { 4, 0 }, // Lightning Shield
/*Bubble Shield*/ { 1, 0 }, // Bubble Shield
/*Flame Shield*/ { 1, 0 }, // Flame Shield
/*Hyudoro*/ { 2, 0 }, // Hyudoro
/*Pogo Spring*/ { 3, 0 }, // Pogo Spring
/*Super Ring*/ { 0, 0 }, // Super Ring
/*Kitchen Sink*/ { 0, 0 }, // Kitchen Sink
/*Drop Target*/ { 2, 0 }, // Drop Target
/*Sneaker x2*/ { 0, 0 }, // Sneaker x2
/*Sneaker x3*/ { 0, 1 }, // Sneaker x3
/*Banana x3*/ { 0, 0 }, // Banana x3
/*Banana x10*/ { 1, 1 }, // Banana x10
/*Orbinaut x3*/ { 2, 0 }, // Orbinaut x3
/*Orbinaut x4*/ { 1, 1 }, // Orbinaut x4
/*Jawz x2*/ { 5, 1 } // Jawz x2
//K L
{ 2, 1 }, // Sneaker
{ 0, 0 }, // Rocket Sneaker
{ 4, 1 }, // Invincibility
{ 0, 0 }, // Banana
{ 1, 0 }, // Eggman Monitor
{ 8, 0 }, // Orbinaut
{ 8, 1 }, // Jawz
{ 6, 1 }, // Mine
{ 2, 0 }, // Land Mine
{ 2, 1 }, // Ballhog
{ 0, 0 }, // Self-Propelled Bomb
{ 2, 1 }, // Grow
{ 0, 0 }, // Shrink
{ 4, 0 }, // Lightning Shield
{ 1, 0 }, // Bubble Shield
{ 1, 0 }, // Flame Shield
{ 2, 0 }, // Hyudoro
{ 3, 0 }, // Pogo Spring
{ 0, 0 }, // Super Ring
{ 0, 0 }, // Kitchen Sink
{ 2, 0 }, // Drop Target
{ 4, 0 }, // Garden Top
{ 0, 0 }, // Sneaker x2
{ 0, 1 }, // Sneaker x3
{ 0, 0 }, // Banana x3
{ 1, 1 }, // Banana x10
{ 2, 0 }, // Orbinaut x3
{ 1, 1 }, // Orbinaut x4
{ 5, 1 } // Jawz x2
};
#define DISTVAR (2048) // Magic number distance for use with item roulette tiers
@ -442,6 +446,7 @@ INT32 K_GetShieldFromItem(INT32 item)
case KITEM_LIGHTNINGSHIELD: return KSHIELD_LIGHTNING;
case KITEM_BUBBLESHIELD: return KSHIELD_BUBBLE;
case KITEM_FLAMESHIELD: return KSHIELD_FLAME;
case KITEM_GARDENTOP: return KSHIELD_TOP;
default: return KSHIELD_NONE;
}
}
@ -715,10 +720,20 @@ INT32 K_KartGetItemOdds(
if (players[i].exiting)
pexiting++;
if (shieldtype != KSHIELD_NONE && shieldtype == K_GetShieldFromItem(players[i].itemtype))
switch (shieldtype)
{
// Don't allow more than one of each shield type at a time
return 0;
case KSHIELD_NONE:
/* Marble Garden Top is not REALLY
a Sonic 3 shield */
case KSHIELD_TOP:
break;
default:
if (shieldtype == K_GetShieldFromItem(players[i].itemtype))
{
// Don't allow more than one of each shield type at a time
return 0;
}
}
if (players[i].position == 1)
@ -1359,7 +1374,13 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against)
if (!mobj->player)
return weight;
if (against && !P_MobjWasRemoved(against) && against->player
if (against && (against->type == MT_GARDENTOP || (against->player && against->player->curshield == KSHIELD_TOP)))
{
/* Players bumping into a Top get zero weight -- the
Top rider is immovable. */
weight = 0;
}
else if (against && !P_MobjWasRemoved(against) && against->player
&& ((!P_PlayerInPain(against->player) && P_PlayerInPain(mobj->player)) // You're hurt
|| (against->player->itemtype == KITEM_BUBBLESHIELD && mobj->player->itemtype != KITEM_BUBBLESHIELD))) // They have a Bubble Shield
{
@ -1947,6 +1968,18 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur
#undef CHAOTIXBANDLEN
}
static boolean K_HasInfiniteTether(player_t *player)
{
switch (player->curshield)
{
case KSHIELD_LIGHTNING:
case KSHIELD_TOP:
return true;
}
return false;
}
/** \brief Updates the player's drafting values once per frame
\param player player object passed from K_KartPlayerThink
@ -1961,7 +1994,7 @@ static void K_UpdateDraft(player_t *player)
UINT8 leniency;
UINT8 i;
if (player->itemtype == KITEM_LIGHTNINGSHIELD)
if (K_HasInfiniteTether(player))
{
// Lightning Shield gets infinite draft distance as its (other) passive effect.
draftdistance = 0;
@ -2399,7 +2432,7 @@ void K_SpawnDriftBoostClipSpark(mobj_t *clip)
spark->momy = clip->momx/2;
}
void K_SpawnNormalSpeedLines(player_t *player)
static void K_SpawnGenericSpeedLines(player_t *player, boolean top)
{
mobj_t *fast = P_SpawnMobj(player->mo->x + (P_RandomRange(PR_DECORATION,-36,36) * player->mo->scale),
player->mo->y + (P_RandomRange(PR_DECORATION,-36,36) * player->mo->scale),
@ -2407,20 +2440,40 @@ void K_SpawnNormalSpeedLines(player_t *player)
MT_FASTLINE);
P_SetTarget(&fast->target, player->mo);
P_InitAngle(fast, K_MomentumAngle(player->mo));
fast->momx = 3*player->mo->momx/4;
fast->momy = 3*player->mo->momy/4;
fast->momz = 3*P_GetMobjZMovement(player->mo)/4;
K_MatchGenericExtraFlags(fast, player->mo);
fast->z += player->mo->sprzoff;
if (player->tripwireLeniency)
if (top)
{
fast->destscale = fast->destscale * 2;
P_SetScale(fast, 3*fast->scale/2);
P_InitAngle(fast, player->mo->angle);
P_SetScale(fast, (fast->destscale =
3 * fast->destscale / 2));
fast->spritexscale = 3*FRACUNIT;
}
else
{
P_InitAngle(fast, K_MomentumAngle(player->mo));
if (player->tripwireLeniency)
{
fast->destscale = fast->destscale * 2;
P_SetScale(fast, 3*fast->scale/2);
}
}
if (player->eggmanexplode)
K_MatchGenericExtraFlags(fast, player->mo);
if (top)
{
fast->color = SKINCOLOR_SUNSLAM;
fast->colorized = true;
fast->renderflags |= RF_ADD;
}
else if (player->eggmanexplode)
{
// Make it red when you have the eggman speed boost
fast->color = SKINCOLOR_RED;
@ -2448,6 +2501,16 @@ void K_SpawnNormalSpeedLines(player_t *player)
}
}
void K_SpawnNormalSpeedLines(player_t *player)
{
K_SpawnGenericSpeedLines(player, false);
}
void K_SpawnGardenTopSpeedLines(player_t *player)
{
K_SpawnGenericSpeedLines(player, true);
}
void K_SpawnInvincibilitySpeedLines(mobj_t *mo)
{
mobj_t *fast = P_SpawnMobjFromMobj(mo,
@ -2546,14 +2609,21 @@ static void K_SpawnGrowShrinkParticles(mobj_t *mo, INT32 timer)
void K_SpawnBumpEffect(mobj_t *mo)
{
mobj_t *top = mo->player ? K_GetGardenTop(mo->player) : NULL;
mobj_t *fx = P_SpawnMobj(mo->x, mo->y, mo->z, MT_BUMP);
if (mo->eflags & MFE_VERTICALFLIP)
fx->eflags |= MFE_VERTICALFLIP;
else
fx->eflags &= ~MFE_VERTICALFLIP;
fx->scale = mo->scale;
S_StartSound(mo, sfx_s3k49);
if (top)
S_StartSound(mo, top->info->attacksound);
else
S_StartSound(mo, sfx_s3k49);
}
static SINT8 K_GlanceAtPlayers(player_t *glancePlayer)
@ -2708,6 +2778,10 @@ void K_KartMoveAnimation(player_t *player)
drift = intsign(player->aizdriftturn);
turndir = 0;
}
else if (player->curshield == KSHIELD_TOP)
{
drift = -turndir;
}
else if (turndir == 0 && drift == 0)
{
// Only try glancing if you're driving straight.
@ -3211,6 +3285,8 @@ boolean K_ApplyOffroad(player_t *player)
{
if (player->invincibilitytimer || player->hyudorotimer || player->sneakertimer)
return false;
if (K_IsRidingFloatingTop(player))
return false;
return true;
}
@ -3218,6 +3294,8 @@ boolean K_SlopeResistance(player_t *player)
{
if (player->invincibilitytimer || player->sneakertimer || player->tiregrease || player->flamedash)
return true;
if (player->curshield == KSHIELD_TOP)
return true;
return false;
}
@ -3281,6 +3359,11 @@ boolean K_WaterRun(mobj_t *mobj)
return false;
}
if (mobj->player->curshield == KSHIELD_TOP)
{
return K_IsHoldingDownTop(mobj->player) == false;
}
if (mobj->player->invincibilitytimer
|| mobj->player->sneakertimer
|| mobj->player->tiregrease
@ -3311,6 +3394,16 @@ boolean K_WaterSkip(mobj_t *mobj)
switch (mobj->type)
{
case MT_PLAYER:
{
if (mobj->player != NULL && mobj->player->curshield == KSHIELD_TOP)
{
// Don't allow
return false;
}
// Allow
break;
}
case MT_ORBINAUT:
case MT_JAWZ:
case MT_BALLHOG:
@ -3486,6 +3579,46 @@ void K_SpawnWaterRunParticles(mobj_t *mobj)
}
}
boolean K_IsRidingFloatingTop(player_t *player)
{
if (player->curshield != KSHIELD_TOP)
{
return false;
}
return !Obj_GardenTopPlayerIsGrinding(player);
}
boolean K_IsHoldingDownTop(player_t *player)
{
if (player->curshield != KSHIELD_TOP)
{
return false;
}
if ((K_GetKartButtons(player) & BT_DRIFT) != BT_DRIFT)
{
return false;
}
return true;
}
mobj_t *K_GetGardenTop(player_t *player)
{
if (player->curshield != KSHIELD_TOP)
{
return NULL;
}
if (player->mo == NULL)
{
return NULL;
}
return player->mo->hnext;
}
static fixed_t K_FlameShieldDashVar(INT32 val)
{
// 1 second = 75% + 50% top speed
@ -3648,7 +3781,7 @@ static void K_GetKartBoostPower(player_t *player)
draftspeed *= 2;
}
if (player->itemtype == KITEM_LIGHTNINGSHIELD)
if (K_HasInfiniteTether(player))
{
// infinite tether
draftspeed *= 2;
@ -3770,6 +3903,10 @@ fixed_t K_GetKartAccel(player_t *player)
if (gametype == GT_BATTLE && player->bumpers <= 0)
k_accel *= 2;
// Marble Garden Top gets 800% accel
if (player->curshield == KSHIELD_TOP)
k_accel *= 8;
return FixedMul(k_accel, (FRACUNIT + player->accelboost) / 4);
}
@ -3859,17 +3996,35 @@ SINT8 K_GetForwardMove(player_t *player)
forwardmove = MAXPLMOVE;
}
if (player->curshield == KSHIELD_TOP)
{
if (forwardmove < 0 ||
(K_GetKartButtons(player) & BT_DRIFT))
{
forwardmove = 0;
}
else
{
forwardmove = MAXPLMOVE;
}
}
return forwardmove;
}
fixed_t K_GetNewSpeed(player_t *player)
{
const fixed_t accelmax = 4000;
const fixed_t p_speed = K_GetKartSpeed(player, true, true);
fixed_t p_speed = K_GetKartSpeed(player, true, true);
fixed_t p_accel = K_GetKartAccel(player);
fixed_t newspeed, oldspeed, finalspeed;
if (player->curshield == KSHIELD_TOP)
{
p_speed = 11 * p_speed / 10;
}
if (K_PlayerUsesBotMovement(player) == true && player->botvars.rubberband > 0)
{
// Acceleration is tied to top speed...
@ -4896,6 +5051,19 @@ fixed_t K_ItemScaleForPlayer(player_t *player)
}
}
fixed_t K_DefaultPlayerRadius(player_t *player)
{
mobj_t *top = K_GetGardenTop(player);
if (top)
{
return top->radius;
}
return FixedMul(player->mo->scale,
player->mo->info->radius);
}
static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, INT32 flags2, fixed_t speed, SINT8 dir)
{
mobj_t *th;
@ -5010,6 +5178,9 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I
th->destscale = th->destscale << 1;
th->scalespeed = abs(th->destscale - th->scale) / (2*TICRATE);
break;
case MT_GARDENTOP:
th->movefactor = finalspeed;
break;
default:
break;
}
@ -5670,8 +5841,11 @@ void K_DriftDustHandling(mobj_t *spawner)
dust->destscale = spawner->scale * 3;
dust->scalespeed = spawner->scale/12;
if (leveltime % 6 == 0)
S_StartSound(spawner, sfx_screec);
if (!spawner->player || !K_GetGardenTop(spawner->player))
{
if (leveltime % 6 == 0)
S_StartSound(spawner, sfx_screec);
}
K_MatchGenericExtraFlags(dust, spawner);
@ -5831,7 +6005,7 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
if (missile) // Shootables
{
if (dir < 0 && mapthing != MT_SPB)
if (dir < 0 && mapthing != MT_SPB && mapthing != MT_GARDENTOP)
{
// Shoot backward
mo = K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + angleOffset, 0, PROJSPEED, dir);
@ -6442,7 +6616,7 @@ void K_DropHnextList(player_t *player, boolean keepshields)
flip = P_MobjFlip(player->mo);
ponground = P_IsObjectOnGround(player->mo);
if (shield != KSHIELD_NONE && !keepshields)
if (shield != KSHIELD_NONE && shield != KSHIELD_TOP && !keepshields)
{
if (shield == KSHIELD_LIGHTNING)
{
@ -6494,6 +6668,9 @@ void K_DropHnextList(player_t *player, boolean keepshields)
orbit = false;
type = MT_EGGMANITEM;
break;
case MT_GARDENTOP:
Obj_GardenTopDestroy(player);
return;
// intentionally do nothing
case MT_ROCKETSNEAKER:
case MT_SINK_SHIELD:
@ -7800,6 +7977,33 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
{
const boolean onground = P_IsObjectOnGround(player->mo);
/* reset sprite offsets :) */
player->mo->sprxoff = 0;
player->mo->spryoff = 0;
player->mo->sprzoff = 0;
player->mo->spritexoffset = 0;
player->mo->spriteyoffset = 0;
if (player->curshield == KSHIELD_TOP)
{
mobj_t *top = K_GetGardenTop(player);
if (top)
{
/* FIXME: I cannot figure out how offset the
player correctly in real time to pivot around
the BOTTOM of the Top. This hack plus the one
in R_PlayerSpriteRotation. */
player->mo->spritexoffset += FixedMul(
FixedDiv(top->height, top->scale),
FINESINE(top->rollangle >> ANGLETOFINESHIFT));
player->mo->sprzoff += top->sprzoff + (
P_GetMobjHead(top) -
P_GetMobjFeet(player->mo));
}
}
K_UpdateOffroad(player);
K_UpdateDraft(player);
K_UpdateEngineSounds(player); // Thanks, VAda!
@ -8244,8 +8448,20 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (cmd->buttons & BT_DRIFT)
{
if (player->curshield == KSHIELD_TOP)
{
if (player->topdriftheld <= GARDENTOP_MAXGRINDTIME)
player->topdriftheld++;
// Squish :)
player->mo->spritexscale = 6*FRACUNIT/4;
player->mo->spriteyscale = 2*FRACUNIT/4;
if (leveltime & 1)
K_SpawnGardenTopSpeedLines(player);
}
// Only allow drifting while NOT trying to do an spindash input.
if ((K_GetKartButtons(player) & BT_EBRAKEMASK) != BT_EBRAKEMASK)
else if ((K_GetKartButtons(player) & BT_EBRAKEMASK) != BT_EBRAKEMASK)
{
player->pflags |= PF_DRIFTINPUT;
}
@ -8382,7 +8598,7 @@ void K_KartResetPlayerColor(player_t *player)
finalise:
if (player->curshield)
if (player->curshield && player->curshield != KSHIELD_TOP)
{
fullbright = true;
}
@ -9027,13 +9243,25 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
if ((currentSpeed <= 0) // Not moving
&& ((K_GetKartButtons(player) & BT_EBRAKEMASK) != BT_EBRAKEMASK) // Not e-braking
&& (player->respawn.state == RESPAWNST_NONE) // Not respawning
&& (player->curshield != KSHIELD_TOP) // Not riding a Top
&& (P_IsObjectOnGround(player->mo) == true)) // On the ground
{
return 0;
}
p_maxspeed = K_GetKartSpeed(player, false, true);
p_speed = min(currentSpeed, (p_maxspeed * 2));
if (player->curshield == KSHIELD_TOP)
{
// Do not downscale turning speed with faster
// movement speed; behaves as if turning in place.
p_speed = 0;
}
else
{
p_speed = min(currentSpeed, (p_maxspeed * 2));
}
weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT));
if (K_PlayerUsesBotMovement(player))
@ -9060,7 +9288,9 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
turnfixed = FixedMul(turnfixed, FRACUNIT + player->handleboost);
}
if (player->mo->eflags & MFE_UNDERWATER)
if (player->curshield == KSHIELD_TOP)
;
else if (player->mo->eflags & MFE_UNDERWATER)
{
fixed_t div = min(FRACUNIT + K_GetUnderwaterStrafeMul(player), 2*FRACUNIT);
turnfixed = FixedDiv(turnfixed, div);
@ -9448,6 +9678,7 @@ void K_KartUpdatePosition(player_t *player)
fixed_t position = 1;
fixed_t oldposition = player->position;
fixed_t i;
INT32 realplayers = 0;
if (player->spectator || !player->mo)
{
@ -9462,6 +9693,8 @@ void K_KartUpdatePosition(player_t *player)
if (!playeringame[i] || players[i].spectator || !players[i].mo)
continue;
realplayers++;
if (gametyperules & GTR_CIRCUIT)
{
if (player->exiting) // End of match standings
@ -9525,6 +9758,33 @@ void K_KartUpdatePosition(player_t *player)
if (oldposition != position) // Changed places?
player->positiondelay = 10; // Position number growth
/* except in FREE PLAY */
if (player->curshield == KSHIELD_TOP &&
(gametyperules & GTR_CIRCUIT) &&
realplayers > 1)
{
/* grace period so you don't fall off INSTANTLY */
if (position == 1 && player->topinfirst < 2*TICRATE)
{
player->topinfirst++;
}
else
{
if (position == 1)
{
Obj_GardenTopThrow(player);
}
else
{
player->topinfirst = 0;
}
}
}
else
{
player->topinfirst = 0;
}
player->position = position;
}
@ -9676,6 +9936,7 @@ void K_KartEbrakeVisuals(player_t *p)
p->mo->hprev->angle = p->mo->angle;
p->mo->hprev->fuse = TICRATE/2; // When we leave spindash for any reason, make sure this bubble goes away soon after.
K_FlipFromObject(p->mo->hprev, p->mo);
p->mo->hprev->sprzoff = p->mo->sprzoff;
}
if (!p->spindash)
@ -10043,6 +10304,11 @@ void K_AdjustPlayerFriction(player_t *player)
player->mo->friction += ((FRACUNIT - prevfriction) / greasetics) * player->tiregrease;
}
if (player->curshield == KSHIELD_TOP)
{
player->mo->friction += 1024;
}
/*
if (K_PlayerEBrake(player) == true)
{
@ -10693,6 +10959,37 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
}
break;
case KITEM_GARDENTOP:
if (ATTACK_IS_DOWN && NO_HYUDORO)
{
if (player->curshield != KSHIELD_TOP)
{
player->topinfirst = 0;
Obj_GardenTopDeploy(player->mo);
}
else
{
if (player->throwdir == -1)
{
mobj_t *top = Obj_GardenTopDestroy(player);
// Fly off the Top at high speed
P_Thrust(player->mo, K_MomentumAngle(player->mo), 80 * mapobjectscale);
P_SetObjectMomZ(player->mo, player->mo->info->height / 8, true);
top->momx = player->mo->momx;
top->momy = player->mo->momy;
top->momz = player->mo->momz;
}
else
{
Obj_GardenTopThrow(player);
S_StartSound(player->mo, sfx_tossed); // play only when actually thrown :^,J
K_PlayAttackTaunt(player->mo);
}
}
}
break;
case KITEM_BUBBLESHIELD:
if (player->curshield != KSHIELD_BUBBLE)
{

View file

@ -69,6 +69,7 @@ void K_SpawnDashDustRelease(player_t *player);
void K_SpawnDriftBoostClip(player_t *player);
void K_SpawnDriftBoostClipSpark(mobj_t *clip);
void K_SpawnNormalSpeedLines(player_t *player);
void K_SpawnGardenTopSpeedLines(player_t *player);
void K_SpawnInvincibilitySpeedLines(mobj_t *mo);
void K_SpawnBumpEffect(mobj_t *mo);
void K_KartMoveAnimation(player_t *player);
@ -146,6 +147,9 @@ boolean K_MovingHorizontally(mobj_t *mobj);
boolean K_WaterRun(mobj_t *mobj);
boolean K_WaterSkip(mobj_t *mobj);
void K_SpawnWaterRunParticles(mobj_t *mobj);
boolean K_IsRidingFloatingTop(player_t *player);
boolean K_IsHoldingDownTop(player_t *player);
mobj_t *K_GetGardenTop(player_t *player);
void K_ApplyTripWire(player_t *player, tripwirestate_t state);
INT16 K_GetSpindashChargeTime(player_t *player);
fixed_t K_GetSpindashChargeSpeed(player_t *player);
@ -172,6 +176,7 @@ UINT8 K_GetOrbinautItemFrame(UINT8 count);
boolean K_IsSPBInGame(void);
void K_KartEbrakeVisuals(player_t *p);
void K_HandleDirectionalInfluence(player_t *player);
fixed_t K_DefaultPlayerRadius(player_t *player);
// sound stuff for lua
void K_PlayAttackTaunt(mobj_t *source);

View file

@ -8,6 +8,14 @@ void Obj_HyudoroThink(mobj_t *actor);
void Obj_HyudoroCenterThink(mobj_t *actor);
void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher);
/* Garden Top */
void Obj_GardenTopDeploy(mobj_t *rider);
mobj_t *Obj_GardenTopThrow(player_t *player);
mobj_t *Obj_GardenTopDestroy(player_t *player);
void Obj_GardenTopThink(mobj_t *top);
void Obj_GardenTopSparkThink(mobj_t *spark);
boolean Obj_GardenTopPlayerIsGrinding(player_t *player);
/* Shrink */
void Obj_PohbeeThinker(mobj_t *pohbee);
void Obj_PohbeeRemoved(mobj_t *pohbee);

View file

@ -1,4 +1,5 @@
hyudoro.c
gardentop.c
shrink.c
item-debris.c
spb.c

614
src/objects/gardentop.c Normal file
View file

@ -0,0 +1,614 @@
#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_local.h"
#include "../s_sound.h"
// TODO: separate from this file
static fixed_t K_FlipZOffset(mobj_t *us, mobj_t *them)
{
fixed_t z = 0;
if (them->eflags & MFE_VERTICALFLIP)
z += them->height;
if (us->eflags & MFE_VERTICALFLIP)
z -= us->height;
return z;
}
#define SPARKCOLOR SKINCOLOR_ROBIN
enum {
TOP_ANCHORED,
TOP_LOOSE,
};
#define topsfx_floating sfx_s3k7d
#define topsfx_grinding sfx_s3k79
#define topsfx_lift sfx_s3ka0
#define rider_top(o) ((o)->hnext)
#define top_mode(o) ((o)->extravalue1)
#define top_float(o) ((o)->lastlook)
#define top_sound(o) ((o)->extravalue2)
#define top_soundtic(o) ((o)->movecount)
/* TOP_ANCHORED */
#define top_rider(o) ((o)->tracer)
/* TOP_LOOSE */
#define top_waveangle(o) ((o)->movedir)
/* wavepause will take mobjinfo reactiontime automatically */
#define top_wavepause(o) ((o)->reactiontime)
#define spark_top(o) ((o)->target)
#define spark_angle(o) ((o)->movedir)
static inline player_t *
get_rider_player (mobj_t *rider)
{
return rider ? rider->player : NULL;
}
static inline player_t *
get_top_rider_player (mobj_t *top)
{
return get_rider_player(top_rider(top));
}
static inline boolean
is_top_grind_input (mobj_t *top)
{
player_t *player = get_top_rider_player(top);
return player && K_IsHoldingDownTop(player);
}
static inline boolean
is_top_grinding (mobj_t *top)
{
if (top_float(top) > 0)
return false;
if (!P_IsObjectOnGround(top))
return false;
return true;
}
static inline fixed_t
grind_spark_base_scale (player_t *player)
{
return FRACUNIT/2 +
player->topdriftheld * FRACUNIT
/ GARDENTOP_MAXGRINDTIME;
}
static inline INT32
get_player_steer_tilt
( player_t * player,
INT32 stages)
{
return player->steering
* stages
// 1 degree for a full turn
/ KART_FULLTURN
* ANG1
// stages is for fractions of a full turn, divide to
// get a fraction of a degree
/ stages
// angle is inverted in reverse gravity
* P_MobjFlip(player->mo);
}
static inline fixed_t
goofy_shake (fixed_t n)
{
return P_RandomRange(PR_DECORATION, -1, 1) * n;
}
static inline void
init_top
( mobj_t * top,
INT32 mode)
{
top_mode(top) = mode;
top_float(top) = 0;
top_sound(top) = sfx_None;
top_waveangle(top) = 0;
}
static void
spawn_spark
( mobj_t * top,
angle_t angle)
{
mobj_t *spark = P_SpawnMobjFromMobj(
top, 0, 0, 0, MT_GARDENTOPSPARK);
P_SetTarget(&spark_top(spark), top);
spark_angle(spark) = angle;
spark->color = SPARKCOLOR;
spark->spriteyscale = 3*FRACUNIT/4;
}
static void
spawn_spark_circle
( mobj_t * top,
UINT8 n)
{
const angle_t a = ANGLE_MAX / n;
UINT8 i;
for (i = 0; i < n; ++i)
{
spawn_spark(top, i * a);
}
}
static void
spawn_grind_spark (mobj_t *top)
{
mobj_t *rider = top_rider(top);
mobj_t *spark;
player_t *player = NULL;
fixed_t x = 0;
fixed_t y = 0;
angle_t angle = top->angle;
if (rider)
{
const fixed_t speed = -20 * top->scale;
angle = K_MomentumAngle(rider);
x = P_ReturnThrustX(rider, angle, speed);
y = P_ReturnThrustY(rider, angle, speed);
player = get_rider_player(rider);
}
spark = P_SpawnMobjFromMobj(
top, x, y, 0, MT_DRIFTSPARK);
spark->momx = x;
spark->momy = y;
P_SetMobjState(spark, S_DRIFTSPARK_A1);
spark->angle = angle;
spark->color = SPARKCOLOR;
if (player)
{
spark->destscale = FixedMul(spark->destscale,
grind_spark_base_scale(player));
P_SetScale(spark, spark->destscale);
}
}
static void
loop_sfx
( mobj_t * top,
sfxenum_t sfx)
{
switch (sfx)
{
case topsfx_floating:
if (S_SoundPlaying(top, sfx))
{
return;
}
break;
case topsfx_grinding:
if ((sfxenum_t)top_sound(top) != sfx)
{
top_soundtic(top) = leveltime;
}
/* FIXME: could this sound just be looped
normally? :face_holding_back_tears: */
if ((leveltime - top_soundtic(top)) % 28 > 0)
{
return;
}
break;
default:
break;
}
S_StartSound(top, sfx);
}
static void
modulate (mobj_t *top)
{
const fixed_t max_hover = top->height / 4;
const fixed_t hover_step = max_hover / 4;
sfxenum_t ambience = sfx_None;
if (is_top_grind_input(top))
{
if (top_float(top) == max_hover)
{
P_SetMobjState(top, S_GARDENTOP_SINKING1);
}
if (top_float(top) > 0)
{
top_float(top) = max(0,
top_float(top) - hover_step);
}
else if (P_IsObjectOnGround(top))
{
spawn_grind_spark(top);
ambience = topsfx_grinding;
}
}
else
{
if (top_float(top) == 0)
{
P_SetMobjState(top, S_GARDENTOP_FLOATING);
S_StopSoundByID(top, topsfx_grinding);
S_StartSound(top, topsfx_lift);
}
if (top_float(top) < max_hover)
{
top_float(top) = min(max_hover,
top_float(top) + hover_step);
}
else
{
ambience = topsfx_floating;
}
}
top->sprzoff = top_float(top) * P_MobjFlip(top);
if (ambience)
{
loop_sfx(top, ambience);
}
top_sound(top) = ambience;
}
static void
tilt (mobj_t *top)
{
player_t *player = get_top_rider_player(top);
INT32 tilt = top->rollangle;
if (is_top_grind_input(top))
{
const angle_t tiltmax = ANGLE_22h;
tilt += get_player_steer_tilt(player, 4);
if (abs(tilt) > tiltmax)
{
tilt = intsign(tilt) * tiltmax;
}
}
else
{
const angle_t decay = ANG1 * 2;
if (abs(tilt) > decay)
{
tilt -= intsign(tilt) * decay;
}
else
{
tilt = 0;
}
}
top->rollangle = tilt;
/* Vibrate left and right if you're about to lose it. */
if (player && player->topinfirst)
{
top->spritexoffset = P_LerpFlip(32*FRACUNIT, 1);
}
else
{
top->spritexoffset = 0;
}
/* Go ABSOLUTELY NUTS if the player is tumbling... */
if (player && player->tumbleBounces > 0)
{
const fixed_t yofs = 48 * FRACUNIT;
const fixed_t ofs3d = 24 * top->scale;
/* spriteyoffset scales, e.g. with K_Squish */
top->spriteyoffset = FixedDiv(
goofy_shake(yofs), top->spriteyscale);
top->sprxoff = goofy_shake(ofs3d);
top->spryoff = goofy_shake(ofs3d);
}
else
{
top->spriteyoffset = 0;
top->sprxoff = 0;
top->spryoff = 0;
}
}
static void
anchor_top (mobj_t *top)
{
mobj_t *rider = top_rider(top);
player_t *player = get_rider_player(rider);
if (player && player->curshield != KSHIELD_TOP)
{
P_RemoveMobj(top);
return;
}
tilt(top);
P_MoveOrigin(top, rider->x, rider->y,
rider->z + K_FlipZOffset(top, rider));
K_GenericExtraFlagsNoZAdjust(top, rider);
/* Copying the Z momentum lets the Top squash and stretch
as it falls with the player. Don't copy the X/Y
momentum because then it would always get slightly
ahead of the player. */
top->momx = 0;
top->momy = 0;
top->momz = rider->momz;
/* The Z momentum can put the Top slightly ahead of the
player in that axis too. It looks cool if the Top
falls below you but not if it bounces up. */
if (top->momz * P_MobjFlip(top) > 0)
{
top->momz = 0;
}
/* match rider's slope tilt */
top->pitch = rider->pitch;
top->roll = rider->roll;
}
static void
loose_think (mobj_t *top)
{
const fixed_t thrustamount = top->movefactor;
const angle_t momangle = K_MomentumAngle(top);
angle_t ang = top->angle;
mobj_t *ghost = P_SpawnGhostMobj(top);
ghost->colorized = true; // already has color!
if (AngleDelta(ang, momangle) > ANGLE_90)
{
top->angle = momangle;
}
if (top_wavepause(top))
{
top_wavepause(top)--;
}
else
{
/* oscillate between +90 and -90 degrees */
ang += AbsAngle(top_waveangle(top)) - ANGLE_90;
}
P_InstaThrust(top, top->angle, thrustamount);
P_Thrust(top, ang, thrustamount);
//top_waveangle(top) = (angle_t)top_waveangle(top) + ANG10;
top_waveangle(top) += ANG10;
/* intangibility grace period */
if (top->threshold > 0)
{
top->threshold--;
}
}
static void
anchor_spark (mobj_t *spark)
{
mobj_t *top = spark_top(spark);
mobj_t *rider = top_rider(top);
player_t *player = get_rider_player(rider);
const angle_t angle = top->angle + spark_angle(spark);
const fixed_t x = P_ReturnThrustX(top, angle, spark->scale);
const fixed_t y = P_ReturnThrustY(top, angle, spark->scale);
/* FIXME: THIS FUNCTION FUCKING SUCKS */
K_FlipFromObject(spark, top);
P_MoveOrigin(spark, top->x + x, top->y + y,
top->z + K_FlipZOffset(spark, top));
spark->angle = angle;
if (player)
{
const fixed_t topspeed =
K_GetKartSpeed(player, false, false);
const fixed_t speed = FixedHypot(
rider->momx, rider->momy);
P_SetScale(spark, FixedMul(top->scale, FRACUNIT/2 +
FixedDiv(speed / 2, topspeed)));
}
}
void
Obj_GardenTopDeploy (mobj_t *rider)
{
player_t *player = rider->player;
mobj_t *top = P_SpawnMobjFromMobj(
rider, 0, 0, 0, MT_GARDENTOP);
init_top(top, TOP_ANCHORED);
top->flags |= MF_NOCLIPHEIGHT;
/* only the player's shadow needs to be rendered */
top->shadowscale = 0;
P_SetTarget(&top_rider(top), rider);
P_SetTarget(&rider_top(rider), top);
if (player)
{
player->curshield = KSHIELD_TOP;
rider->radius = K_DefaultPlayerRadius(player);
}
spawn_spark_circle(top, 6);
}
mobj_t *
Obj_GardenTopThrow (player_t *player)
{
mobj_t *top = K_GetGardenTop(player);
if (top)
{
const fixed_t oldfloat = top_float(top);
const fixed_t height = top->height;
K_UpdateHnextList(player, true);
/* Sucks that another one needs to be spawned but
this way, the throwing function can be used. */
top = K_ThrowKartItem(
player, true, MT_GARDENTOP, 1, 0, 0);
init_top(top, TOP_LOOSE);
top_float(top) = oldfloat;
top_waveangle(top) = 0;
/* prevents it from hitting us on its way out */
top->threshold = 20;
/* ensure it's tangible */
top->flags &= ~(MF_NOCLIPTHING);
/* Put player PHYSICALLY on top. While riding the
Top, player collision was used and the player
technically remained on the ground. Now they
should fall off. */
P_SetOrigin(player->mo, player->mo->x, player->mo->y,
player->mo->z + height * P_MobjFlip(player->mo));
if (player->itemamount > 0)
player->itemamount--;
if (player->itemamount <= 0)
player->itemtype = KITEM_NONE;
player->curshield = KSHIELD_NONE;
player->mo->radius = K_DefaultPlayerRadius(player);
}
return top;
}
mobj_t *
Obj_GardenTopDestroy (player_t *player)
{
mobj_t *top = Obj_GardenTopThrow(player);
if (top)
{
/* kill kill kill die die die */
P_KillMobj(top, NULL, NULL, DMG_NORMAL);
}
return top;
}
void
Obj_GardenTopThink (mobj_t *top)
{
modulate(top);
switch (top_mode(top))
{
case TOP_ANCHORED:
if (top_rider(top))
{
anchor_top(top);
}
break;
case TOP_LOOSE:
loose_think(top);
break;
}
}
void
Obj_GardenTopSparkThink (mobj_t *spark)
{
mobj_t *top = spark_top(spark);
if (!top)
{
P_RemoveMobj(spark);
return;
}
anchor_spark(spark);
if (is_top_grinding(top))
{
spark->renderflags ^= RF_DONTDRAW;
}
else
{
spark->renderflags |= RF_DONTDRAW;
}
}
boolean
Obj_GardenTopPlayerIsGrinding (player_t *player)
{
mobj_t *top = K_GetGardenTop(player);
return top ? is_top_grinding(top) : false;
}

View file

@ -143,6 +143,7 @@ void Obj_OrbinautThink(mobj_t *th)
boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
{
boolean damageitem = false;
boolean tumbleitem = false;
boolean sprung = false;
if ((orbinaut_selfdelay(t1) > 0 && t2->hitlag > 0)
@ -173,6 +174,11 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
return true;
}
if (t1->type == MT_GARDENTOP)
{
tumbleitem = true;
}
if (t2->player)
{
if ((t2->player->flashing > 0 && t2->hitlag == 0)
@ -190,7 +196,8 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
else
{
// Player Damage
P_DamageMobj(t2, t1, t1->target, 1, DMG_WIPEOUT|DMG_WOMBO);
P_DamageMobj(t2, t1, t1->target, 1, DMG_WOMBO |
(tumbleitem ? DMG_TUMBLE : DMG_WIPEOUT));
K_KartBouncing(t2, t1);
S_StartSound(t2, sfx_s3k7b);
}
@ -233,6 +240,11 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
damageitem = true;
}
if (t1->type == MT_GARDENTOP)
{
damageitem = false;
}
if (damageitem)
{
// This Item Damage

View file

@ -2014,6 +2014,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
player->emeralds = 0;
K_CheckEmeralds(source->player);
}
/* Drop "shield" immediately on contact. */
if (source->player->curshield == KSHIELD_TOP)
{
Obj_GardenTopDestroy(source->player);
}
}
else
{

View file

@ -866,6 +866,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
&& (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ
|| tmthing->type == MT_BANANA || tmthing->type == MT_EGGMANITEM || tmthing->type == MT_BALLHOG
|| tmthing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || tmthing->type == MT_SINK
|| tmthing->type == MT_GARDENTOP
|| (tmthing->type == MT_PLAYER && thing->target != tmthing)))
{
// see if it went over / under
@ -881,6 +882,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
&& (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ
|| thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG
|| thing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || thing->type == MT_SINK
|| thing->type == MT_GARDENTOP
|| (thing->type == MT_PLAYER && tmthing->target != thing)))
{
// see if it went over / under
@ -901,6 +903,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
&& (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ
|| tmthing->type == MT_BANANA || tmthing->type == MT_EGGMANITEM || tmthing->type == MT_BALLHOG
|| tmthing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || tmthing->type == MT_SINK
|| tmthing->type == MT_GARDENTOP
|| (tmthing->type == MT_PLAYER)))
{
// see if it went over / under
@ -915,6 +918,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
&& (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ
|| thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG
|| thing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || thing->type == MT_SINK
|| thing->type == MT_GARDENTOP
|| (thing->type == MT_PLAYER)))
{
// see if it went over / under
@ -932,7 +936,8 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return BMIT_CONTINUE;
if (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ
|| tmthing->type == MT_ORBINAUT_SHIELD || tmthing->type == MT_JAWZ_SHIELD)
|| tmthing->type == MT_ORBINAUT_SHIELD || tmthing->type == MT_JAWZ_SHIELD
|| tmthing->type == MT_GARDENTOP)
{
// see if it went over / under
if (tmthing->z > thing->z + thing->height)
@ -943,7 +948,8 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return Obj_OrbinautJawzCollide(tmthing, thing) ? BMIT_CONTINUE : BMIT_ABORT;
}
else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ
|| thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD)
|| thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD
|| thing->type == MT_GARDENTOP)
{
// see if it went over / under
if (tmthing->z > thing->z + thing->height)
@ -2842,6 +2848,11 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
thing->terrain = NULL;
}
if (thing->player && K_IsRidingFloatingTop(thing->player))
{
stairjank = false;
}
/* FIXME: slope step down (even up) has some false
positives, so just ignore them entirely. */
if (stairjank && !oldslope && !thing->standingslope &&

View file

@ -1133,7 +1133,11 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
gravityadd = FixedMul(TUMBLEGRAVITY, gravityadd);
}
if (mo->player->fastfall != 0)
if (K_IsHoldingDownTop(mo->player))
{
gravityadd = (5*gravityadd)/2;
}
else if (mo->player->fastfall != 0)
{
// Fast falling
gravityadd *= 4;
@ -1734,6 +1738,7 @@ void P_XYMovement(mobj_t *mo)
switch (mo->type)
{
case MT_ORBINAUT: // Orbinaut speed decreasing
case MT_GARDENTOP:
if (mo->health > 1)
{
S_StartSound(mo, mo->info->attacksound);
@ -6444,6 +6449,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
return false;
}
/* FALLTHRU */
case MT_GARDENTOP:
case MT_ORBINAUT_SHIELD:
case MT_BANANA_SHIELD:
case MT_EGGMANITEM_SHIELD:
@ -7152,7 +7158,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
}
}
break;
case MT_TRIPWIREBOOST:
case MT_TRIPWIREBOOST: {
mobj_t *top;
fixed_t newHeight;
fixed_t newScale;
if (!mobj->target || !mobj->target->health
|| !mobj->target->player || !mobj->target->player->tripwireLeniency)
{
@ -7160,10 +7170,21 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
return false;
}
newHeight = mobj->target->height;
newScale = mobj->target->scale;
top = K_GetGardenTop(mobj->target->player);
if (top)
{
newHeight += 5 * top->height / 4;
newScale = FixedMul(newScale, FixedDiv(newHeight / 2, mobj->target->height));
}
mobj->angle = K_MomentumAngle(mobj->target);
P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (mobj->target->height >> 1));
mobj->destscale = mobj->target->scale;
P_SetScale(mobj, mobj->target->scale);
P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (newHeight / 2));
mobj->destscale = newScale;
P_SetScale(mobj, newScale);
if (mobj->extravalue1)
{
@ -7235,6 +7256,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
}
}
break;
}
case MT_BOOSTFLAME:
if (!mobj->target || !mobj->target->health)
{
@ -7806,6 +7828,16 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
}
break;
}
case MT_GARDENTOP:
{
Obj_GardenTopThink(mobj);
break;
}
case MT_GARDENTOPSPARK:
{
Obj_GardenTopSparkThink(mobj);
break;
}
case MT_HYUDORO:
{
Obj_HyudoroThink(mobj);
@ -9851,6 +9883,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing)
case MT_BUBBLESHIELD:
case MT_BUBBLESHIELDTRAP:
case MT_FLAMESHIELD:
case MT_GARDENTOP:
thing->shadowscale = FRACUNIT;
break;
case MT_RING:

View file

@ -378,6 +378,8 @@ static void P_NetArchivePlayers(void)
WRITEUINT8(save_p, players[i].kickstartaccel);
WRITEUINT8(save_p, players[i].stairjank);
WRITEUINT8(save_p, players[i].topdriftheld);
WRITEUINT8(save_p, players[i].topinfirst);
WRITEUINT8(save_p, players[i].shrinkLaserDelay);
@ -674,6 +676,8 @@ static void P_NetUnArchivePlayers(void)
players[i].kickstartaccel = READUINT8(save_p);
players[i].stairjank = READUINT8(save_p);
players[i].topdriftheld = READUINT8(save_p);
players[i].topinfirst = READUINT8(save_p);
players[i].shrinkLaserDelay = READUINT8(save_p);

View file

@ -4416,7 +4416,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
switch (special)
{
case 1: // Damage (Generic)
if (roversector || P_MobjReadyToTrigger(player->mo, sector))
if (!K_IsRidingFloatingTop(player) && (roversector || P_MobjReadyToTrigger(player->mo, sector)))
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL);
break;
case 2: // Damage (Water) // SRB2kart - These three damage types are now offroad sectors
@ -4424,7 +4424,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
case 4: // Damage (Electrical)
break;
case 5: // Spikes
if (roversector || P_MobjReadyToTrigger(player->mo, sector))
if (!K_IsRidingFloatingTop(player) && (roversector || P_MobjReadyToTrigger(player->mo, sector)))
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL);
break;
case 6: // Death Pit (Camera Mod)

View file

@ -44,6 +44,14 @@ INT32 P_AltFlip(INT32 n, tic_t tics)
return leveltime % (2 * tics) < tics ? n : -(n);
}
// Please read p_tick.h
INT32 P_LerpFlip(INT32 n, tic_t tics)
{
const tic_t w = 2 * tics;
return P_AltFlip(((leveltime % w) - tics) * n, w);
}
//
// THINKERS
// All thinkers should be allocated by Z_Calloc

View file

@ -35,4 +35,12 @@ mobj_t *P_SetTarget(mobj_t **mo, mobj_t *target); // killough 11/98
INT32 P_AltFlip(INT32 value, tic_t tics);
#define P_RandomFlip(value) P_AltFlip(value, 1)
// Multiply value back and forth between -(tics) and +(tics).
// Example output P_ModulateFlip(2, 2):
// Tic: 0 1 2 3 4 5 6 7 8
// Val: -4 -2 0 2 4 2 0 -2 -4
// A half cycle (one direction) takes 2 * tics.
// A full cycle takes 4 * tics.
INT32 P_LerpFlip(INT32 value, tic_t tics);
#endif

View file

@ -1801,7 +1801,7 @@ static void P_3dMovement(player_t *player)
// Get the old momentum; this will be needed at the end of the function! -SH
oldMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0);
if (player->stairjank > 8 && leveltime & 3)
if ((player->stairjank > 8 && leveltime & 3) || K_IsRidingFloatingTop(player))
{
movepushangle = K_MomentumAngle(player->mo);
}
@ -1884,6 +1884,7 @@ static void P_3dMovement(player_t *player)
if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration...
movepushforward = FixedMul(movepushforward, player->mo->movefactor);
if (player->curshield != KSHIELD_TOP)
{
INT32 a = K_GetUnderwaterTurnAdjust(player);
INT32 adj = 0;
@ -1967,6 +1968,24 @@ static void P_3dMovement(player_t *player)
player->mo->momx += totalthrust.x;
player->mo->momy += totalthrust.y;
// Releasing a drift while on the Top translates all your
// momentum (and even then some) into whichever direction
// you're facing
if (onground && player->curshield == KSHIELD_TOP && (K_GetKartButtons(player) & BT_DRIFT) != BT_DRIFT && (player->oldcmd.buttons & BT_DRIFT))
{
const fixed_t gmin = FRACUNIT/4;
const fixed_t gmax = 5*FRACUNIT/2;
const fixed_t grindfactor = (gmax - gmin) / GARDENTOP_MAXGRINDTIME;
const fixed_t grindscale = gmin + (player->topdriftheld * grindfactor);
const fixed_t speed = R_PointToDist2(0, 0, player->mo->momx, player->mo->momy);
P_InstaThrust(player->mo, player->mo->angle, FixedMul(speed, grindscale));
player->topdriftheld = 0;/* reset after release */
}
if (!onground)
{
const fixed_t airspeedcap = (50*mapobjectscale);
@ -3169,6 +3188,13 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
}
/* The Top is Big Large so zoom out */
if (player->curshield == KSHIELD_TOP)
{
camdist += 40 * mapobjectscale;
camheight += 40 * mapobjectscale;
}
if (!resetcalled && (leveltime >= introtime && timeover != 2)
&& (t_cam_rotate[num] != -42))
{

View file

@ -42,6 +42,8 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer)
angle_t rollAngle = 0;
mobj_t *top = K_GetGardenTop(player);
if (player->mo->eflags & MFE_UNDERWATER)
{
rollAngle -= player->underwatertilt;
@ -61,6 +63,14 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer)
(17 / player->stairjank));
}
if (top)
{
/* FIXME: why does it not look right at more acute
angles without this? There's a related hack to
spritexoffset in K_KartPlayerThink. */
rollAngle += 3 * (INT32)top->rollangle / 2;
}
return rollAngle;
}