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 #endif
// SRB2kart // 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_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_rocketsneaker = CVAR_INIT ("rocketsneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_invincibility = CVAR_INIT ("invincibility", "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_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_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_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_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_selfpropelledbomb = CVAR_INIT ("selfpropelledbomb", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_grow = CVAR_INIT ("grow", "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_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_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_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_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_dualsneaker = CVAR_INIT ("dualsneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_triplesneaker = CVAR_INIT ("triplesneaker", "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; extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_maxplayers, cv_respawntime;
// SRB2kart items // SRB2kart items
extern consvar_t cv_superring, cv_sneaker, cv_rocketsneaker, cv_invincibility, cv_banana; extern consvar_t
extern consvar_t cv_eggmanmonitor, cv_orbinaut, cv_jawz, cv_mine, cv_landmine, cv_droptarget; cv_sneaker,
extern consvar_t cv_ballhog, cv_selfpropelledbomb, cv_grow, cv_shrink; cv_rocketsneaker,
extern consvar_t cv_lightningshield, cv_bubbleshield, cv_flameshield; cv_invincibility,
extern consvar_t cv_hyudoro, cv_pogospring, cv_kitchensink; 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
extern consvar_t cv_tripleorbinaut, cv_quadorbinaut, cv_dualjawz; cv_dualsneaker,
cv_triplesneaker,
cv_triplebanana,
cv_decabanana,
cv_tripleorbinaut,
cv_quadorbinaut,
cv_dualjawz;
extern consvar_t cv_kartminimap; extern consvar_t cv_kartminimap;
extern consvar_t cv_kartcheck; extern consvar_t cv_kartcheck;

View file

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

View file

@ -3740,6 +3740,14 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_FLAMESHIELDLINE3", "S_FLAMESHIELDLINE3",
"S_FLAMESHIELDFLASH", "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 // Caked-Up Booty-Sheet Ghost
"S_HYUDORO", "S_HYUDORO",
@ -5346,6 +5354,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_FLAMESHIELDUNDERLAY", "MT_FLAMESHIELDUNDERLAY",
"MT_FLAMESHIELDPAPER", "MT_FLAMESHIELDPAPER",
"MT_BUBBLESHIELDTRAP", "MT_BUBBLESHIELDTRAP",
"MT_GARDENTOP",
"MT_GARDENTOPSPARK",
"MT_HYUDORO", "MT_HYUDORO",
"MT_HYUDORO_CENTER", "MT_HYUDORO_CENTER",
@ -6668,6 +6678,7 @@ struct int_const_s const INT_CONST[] = {
{"KSHIELD_LIGHTNING",KSHIELD_LIGHTNING}, {"KSHIELD_LIGHTNING",KSHIELD_LIGHTNING},
{"KSHIELD_BUBBLE",KSHIELD_BUBBLE}, {"KSHIELD_BUBBLE",KSHIELD_BUBBLE},
{"KSHIELD_FLAME",KSHIELD_FLAME}, {"KSHIELD_FLAME",KSHIELD_FLAME},
{"KSHIELD_TOP",KSHIELD_TOP},
{"NUMKARTSHIELDS",NUMKARTSHIELDS}, {"NUMKARTSHIELDS",NUMKARTSHIELDS},
// kartspinoutflags_t // 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_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_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_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 {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 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 { // MT_HYUDORO
-1, // doomednum -1, // doomednum
S_HYUDORO, // spawnstate S_HYUDORO, // spawnstate

View file

@ -4750,6 +4750,14 @@ typedef enum state
S_FLAMESHIELDLINE3, S_FLAMESHIELDLINE3,
S_FLAMESHIELDFLASH, 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 // Caked-Up Booty-Sheet Ghost
S_HYUDORO, S_HYUDORO,
@ -6392,6 +6400,8 @@ typedef enum mobj_type
MT_FLAMESHIELDUNDERLAY, MT_FLAMESHIELDUNDERLAY,
MT_FLAMESHIELDPAPER, MT_FLAMESHIELDPAPER,
MT_BUBBLESHIELDTRAP, MT_BUBBLESHIELDTRAP,
MT_GARDENTOP,
MT_GARDENTOPSPARK,
MT_HYUDORO, MT_HYUDORO,
MT_HYUDORO_CENTER, 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) if (t1->type == MT_BANANA && t1->health > 1)
S_StartSound(t2, sfx_bsnipe); S_StartSound(t2, sfx_bsnipe);
damageitem = true;
if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD) if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD)
{ {
// Melt item // Melt item
S_StartSound(t2, sfx_s3k43); S_StartSound(t2, sfx_s3k43);
} }
else if (K_IsRidingFloatingTop(t2->player))
{
// Float over silly banana
damageitem = false;
}
else else
{ {
P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL|DMG_WOMBO); P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL|DMG_WOMBO);
} }
damageitem = true;
} }
else if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD else if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD
|| t2->type == MT_ORBINAUT || t2->type == MT_ORBINAUT_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 // Clash instead of damage if both parties have any of these conditions
t1Condition = (K_IsBigger(t1, t2) == true) t1Condition = (K_IsBigger(t1, t2) == true)
|| (t1->player->invincibilitytimer > 0) || (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) t2Condition = (K_IsBigger(t2, t1) == true)
|| (t2->player->invincibilitytimer > 0) || (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) 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_itemmulsticker[2];
static patch_t *kp_itemx; 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_sneaker[2];
static patch_t *kp_rocketsneaker[2]; static patch_t *kp_rocketsneaker[2];
static patch_t *kp_invincibility[13]; 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_jawz[2];
static patch_t *kp_mine[2]; static patch_t *kp_mine[2];
static patch_t *kp_landmine[2]; static patch_t *kp_landmine[2];
static patch_t *kp_droptarget[2];
static patch_t *kp_ballhog[2]; static patch_t *kp_ballhog[2];
static patch_t *kp_selfpropelledbomb[2]; static patch_t *kp_selfpropelledbomb[2];
static patch_t *kp_grow[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_flameshield[2];
static patch_t *kp_hyudoro[2]; static patch_t *kp_hyudoro[2];
static patch_t *kp_pogospring[2]; static patch_t *kp_pogospring[2];
static patch_t *kp_superring[2];
static patch_t *kp_kitchensink[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]; static patch_t *kp_check[6];
@ -390,7 +391,7 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_itemmulsticker[0], "K_ITMUL"); HU_UpdatePatch(&kp_itemmulsticker[0], "K_ITMUL");
HU_UpdatePatch(&kp_itemx, "K_ITX"); 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_sneaker[0], "K_ITSHOE");
HU_UpdatePatch(&kp_rocketsneaker[0], "K_ITRSHE"); 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_jawz[0], "K_ITJAWZ");
HU_UpdatePatch(&kp_mine[0], "K_ITMINE"); HU_UpdatePatch(&kp_mine[0], "K_ITMINE");
HU_UpdatePatch(&kp_landmine[0], "K_ITLNDM"); HU_UpdatePatch(&kp_landmine[0], "K_ITLNDM");
HU_UpdatePatch(&kp_droptarget[0], "K_ITDTRG");
HU_UpdatePatch(&kp_ballhog[0], "K_ITBHOG"); HU_UpdatePatch(&kp_ballhog[0], "K_ITBHOG");
HU_UpdatePatch(&kp_selfpropelledbomb[0], "K_ITSPB"); HU_UpdatePatch(&kp_selfpropelledbomb[0], "K_ITSPB");
HU_UpdatePatch(&kp_grow[0], "K_ITGROW"); 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_flameshield[0], "K_ITFLMS");
HU_UpdatePatch(&kp_hyudoro[0], "K_ITHYUD"); HU_UpdatePatch(&kp_hyudoro[0], "K_ITHYUD");
HU_UpdatePatch(&kp_pogospring[0], "K_ITPOGO"); HU_UpdatePatch(&kp_pogospring[0], "K_ITPOGO");
HU_UpdatePatch(&kp_superring[0], "K_ITRING");
HU_UpdatePatch(&kp_kitchensink[0], "K_ITSINK"); 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"); sprintf(buffer, "FSMFGxxx");
for (i = 0; i < 104; i++) for (i = 0; i < 104; i++)
@ -447,7 +449,7 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_itemtimer[1], "K_ISIMER"); HU_UpdatePatch(&kp_itemtimer[1], "K_ISIMER");
HU_UpdatePatch(&kp_itemmulsticker[1], "K_ISMUL"); 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_sneaker[1], "K_ISSHOE");
HU_UpdatePatch(&kp_rocketsneaker[1], "K_ISRSHE"); HU_UpdatePatch(&kp_rocketsneaker[1], "K_ISRSHE");
sprintf(buffer, "K_ISINVx"); sprintf(buffer, "K_ISINVx");
@ -462,7 +464,6 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_jawz[1], "K_ISJAWZ"); HU_UpdatePatch(&kp_jawz[1], "K_ISJAWZ");
HU_UpdatePatch(&kp_mine[1], "K_ISMINE"); HU_UpdatePatch(&kp_mine[1], "K_ISMINE");
HU_UpdatePatch(&kp_landmine[1], "K_ISLNDM"); HU_UpdatePatch(&kp_landmine[1], "K_ISLNDM");
HU_UpdatePatch(&kp_droptarget[1], "K_ISDTRG");
HU_UpdatePatch(&kp_ballhog[1], "K_ISBHOG"); HU_UpdatePatch(&kp_ballhog[1], "K_ISBHOG");
HU_UpdatePatch(&kp_selfpropelledbomb[1], "K_ISSPB"); HU_UpdatePatch(&kp_selfpropelledbomb[1], "K_ISSPB");
HU_UpdatePatch(&kp_grow[1], "K_ISGROW"); 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_flameshield[1], "K_ISFLMS");
HU_UpdatePatch(&kp_hyudoro[1], "K_ISHYUD"); HU_UpdatePatch(&kp_hyudoro[1], "K_ISHYUD");
HU_UpdatePatch(&kp_pogospring[1], "K_ISPOGO"); HU_UpdatePatch(&kp_pogospring[1], "K_ISPOGO");
HU_UpdatePatch(&kp_superring[1], "K_ISRING");
HU_UpdatePatch(&kp_kitchensink[1], "K_ISSINK"); 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"); sprintf(buffer, "FSMFSxxx");
for (i = 0; i < 104; i++) for (i = 0; i < 104; i++)
@ -662,8 +665,6 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny)
return (tiny ? "K_ISMINE" : "K_ITMINE"); return (tiny ? "K_ISMINE" : "K_ITMINE");
case KITEM_LANDMINE: case KITEM_LANDMINE:
return (tiny ? "K_ISLNDM" : "K_ITLNDM"); return (tiny ? "K_ISLNDM" : "K_ITLNDM");
case KITEM_DROPTARGET:
return (tiny ? "K_ISDTRG" : "K_ITDTRG");
case KITEM_BALLHOG: case KITEM_BALLHOG:
return (tiny ? "K_ISBHOG" : "K_ITBHOG"); return (tiny ? "K_ISBHOG" : "K_ITBHOG");
case KITEM_SPB: case KITEM_SPB:
@ -686,6 +687,10 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny)
return (tiny ? "K_ISRING" : "K_ITRING"); return (tiny ? "K_ISRING" : "K_ITRING");
case KITEM_KITCHENSINK: case KITEM_KITCHENSINK:
return (tiny ? "K_ISSINK" : "K_ITSINK"); 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: case KRITEM_TRIPLEORBINAUT:
return (tiny ? "K_ISORBN" : "K_ITORB3"); return (tiny ? "K_ISORBN" : "K_ITORB3");
case KRITEM_QUADORBINAUT: case KRITEM_QUADORBINAUT:
@ -721,6 +726,7 @@ static patch_t *K_GetCachedItemPatch(INT32 item, UINT8 offset)
kp_superring, kp_superring,
kp_kitchensink, kp_kitchensink,
kp_droptarget, kp_droptarget,
kp_gardentop,
}; };
if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS)) if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS))
@ -4456,6 +4462,7 @@ static void K_drawDistributionDebugger(void)
kp_superring[1], kp_superring[1],
kp_kitchensink[1], kp_kitchensink[1],
kp_droptarget[1], kp_droptarget[1],
kp_gardentop[1],
kp_sneaker[1], kp_sneaker[1],
kp_sneaker[1], kp_sneaker[1],

View file

@ -218,7 +218,6 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_jawz); CV_RegisterVar(&cv_jawz);
CV_RegisterVar(&cv_mine); CV_RegisterVar(&cv_mine);
CV_RegisterVar(&cv_landmine); CV_RegisterVar(&cv_landmine);
CV_RegisterVar(&cv_droptarget);
CV_RegisterVar(&cv_ballhog); CV_RegisterVar(&cv_ballhog);
CV_RegisterVar(&cv_selfpropelledbomb); CV_RegisterVar(&cv_selfpropelledbomb);
CV_RegisterVar(&cv_grow); CV_RegisterVar(&cv_grow);
@ -230,6 +229,8 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_pogospring); CV_RegisterVar(&cv_pogospring);
CV_RegisterVar(&cv_superring); CV_RegisterVar(&cv_superring);
CV_RegisterVar(&cv_kitchensink); CV_RegisterVar(&cv_kitchensink);
CV_RegisterVar(&cv_droptarget);
CV_RegisterVar(&cv_gardentop);
CV_RegisterVar(&cv_dualsneaker); CV_RegisterVar(&cv_dualsneaker);
CV_RegisterVar(&cv_triplesneaker); CV_RegisterVar(&cv_triplesneaker);
@ -336,6 +337,7 @@ consvar_t *KartItemCVars[NUMKARTRESULTS-1] =
&cv_superring, &cv_superring,
&cv_kitchensink, &cv_kitchensink,
&cv_droptarget, &cv_droptarget,
&cv_gardentop,
&cv_dualsneaker, &cv_dualsneaker,
&cv_triplesneaker, &cv_triplesneaker,
&cv_triplebanana, &cv_triplebanana,
@ -350,68 +352,70 @@ consvar_t *KartItemCVars[NUMKARTRESULTS-1] =
// Less ugly 2D arrays // Less ugly 2D arrays
static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
{ {
//P-Odds 0 1 2 3 4 5 6 7 //B C D E F G H I
/*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker { 0, 0, 2, 3, 4, 0, 0, 0 }, // Sneaker
/*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker { 0, 0, 0, 0, 0, 3, 4, 5 }, // Rocket Sneaker
/*Invincibility*/ { 0, 0, 0, 0, 3, 4, 5, 7 }, // Invincibility { 0, 0, 0, 0, 2, 5, 5, 7 }, // Invincibility
/*Banana*/ { 2, 3, 1, 0, 0, 0, 0, 0 }, // Banana { 2, 3, 1, 0, 0, 0, 0, 0 }, // Banana
/*Eggman Monitor*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor { 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor
/*Orbinaut*/ { 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut { 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut
/*Jawz*/ { 0, 4, 2, 1, 0, 0, 0, 0 }, // Jawz { 0, 4, 2, 1, 0, 0, 0, 0 }, // Jawz
/*Mine*/ { 0, 3, 3, 1, 0, 0, 0, 0 }, // Mine { 0, 3, 3, 1, 0, 0, 0, 0 }, // Mine
/*Land Mine*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Land Mine { 3, 0, 0, 0, 0, 0, 0, 0 }, // Land Mine
/*Ballhog*/ { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog
/*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Self-Propelled Bomb { 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb
/*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow { 0, 0, 0, 0, 2, 5, 0, 0 }, // Grow
/*Shrink*/ { 0, 0, 0, 0, 0, 1, 3, 2 }, // Shrink { 0, 0, 0, 0, 0, 2, 4, 2 }, // Shrink
/*Lightning Shield*/ { 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield { 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield
/*Bubble Shield*/ { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield
/*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield
/*Hyudoro*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Hyudoro { 3, 0, 0, 0, 0, 0, 0, 0 }, // Hyudoro
/*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring
/*Super Ring*/ { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring
/*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
/*Drop Target*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target
/*Sneaker x2*/ { 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2 { 0, 0, 0, 3, 5, 0, 0, 0 }, // Garden Top
/*Sneaker x3*/ { 0, 0, 0, 1, 6, 9, 5, 0 }, // Sneaker x3 { 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2
/*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 { 0, 0, 0, 0, 4, 4, 4, 0 }, // Sneaker x3
/*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3
/*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10
/*Orbinaut x4*/ { 0, 0, 0, 2, 0, 0, 0, 0 }, // Orbinaut x4 { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3
/*Jawz x2*/ { 0, 0, 1, 2, 1, 0, 0, 0 } // Jawz x2 { 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] = static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
{ {
//P-Odds 0 1 //K L
/*Sneaker*/ { 2, 1 }, // Sneaker { 2, 1 }, // Sneaker
/*Rocket Sneaker*/ { 0, 0 }, // Rocket Sneaker { 0, 0 }, // Rocket Sneaker
/*Invincibility*/ { 4, 1 }, // Invincibility { 4, 1 }, // Invincibility
/*Banana*/ { 0, 0 }, // Banana { 0, 0 }, // Banana
/*Eggman Monitor*/ { 1, 0 }, // Eggman Monitor { 1, 0 }, // Eggman Monitor
/*Orbinaut*/ { 8, 0 }, // Orbinaut { 8, 0 }, // Orbinaut
/*Jawz*/ { 8, 1 }, // Jawz { 8, 1 }, // Jawz
/*Mine*/ { 6, 1 }, // Mine { 6, 1 }, // Mine
/*Land Mine*/ { 2, 0 }, // Land Mine { 2, 0 }, // Land Mine
/*Ballhog*/ { 2, 1 }, // Ballhog { 2, 1 }, // Ballhog
/*Self-Propelled Bomb*/ { 0, 0 }, // Self-Propelled Bomb { 0, 0 }, // Self-Propelled Bomb
/*Grow*/ { 2, 1 }, // Grow { 2, 1 }, // Grow
/*Shrink*/ { 0, 0 }, // Shrink { 0, 0 }, // Shrink
/*Lightning Shield*/ { 4, 0 }, // Lightning Shield { 4, 0 }, // Lightning Shield
/*Bubble Shield*/ { 1, 0 }, // Bubble Shield { 1, 0 }, // Bubble Shield
/*Flame Shield*/ { 1, 0 }, // Flame Shield { 1, 0 }, // Flame Shield
/*Hyudoro*/ { 2, 0 }, // Hyudoro { 2, 0 }, // Hyudoro
/*Pogo Spring*/ { 3, 0 }, // Pogo Spring { 3, 0 }, // Pogo Spring
/*Super Ring*/ { 0, 0 }, // Super Ring { 0, 0 }, // Super Ring
/*Kitchen Sink*/ { 0, 0 }, // Kitchen Sink { 0, 0 }, // Kitchen Sink
/*Drop Target*/ { 2, 0 }, // Drop Target { 2, 0 }, // Drop Target
/*Sneaker x2*/ { 0, 0 }, // Sneaker x2 { 4, 0 }, // Garden Top
/*Sneaker x3*/ { 0, 1 }, // Sneaker x3 { 0, 0 }, // Sneaker x2
/*Banana x3*/ { 0, 0 }, // Banana x3 { 0, 1 }, // Sneaker x3
/*Banana x10*/ { 1, 1 }, // Banana x10 { 0, 0 }, // Banana x3
/*Orbinaut x3*/ { 2, 0 }, // Orbinaut x3 { 1, 1 }, // Banana x10
/*Orbinaut x4*/ { 1, 1 }, // Orbinaut x4 { 2, 0 }, // Orbinaut x3
/*Jawz x2*/ { 5, 1 } // Jawz x2 { 1, 1 }, // Orbinaut x4
{ 5, 1 } // Jawz x2
}; };
#define DISTVAR (2048) // Magic number distance for use with item roulette tiers #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_LIGHTNINGSHIELD: return KSHIELD_LIGHTNING;
case KITEM_BUBBLESHIELD: return KSHIELD_BUBBLE; case KITEM_BUBBLESHIELD: return KSHIELD_BUBBLE;
case KITEM_FLAMESHIELD: return KSHIELD_FLAME; case KITEM_FLAMESHIELD: return KSHIELD_FLAME;
case KITEM_GARDENTOP: return KSHIELD_TOP;
default: return KSHIELD_NONE; default: return KSHIELD_NONE;
} }
} }
@ -715,10 +720,20 @@ INT32 K_KartGetItemOdds(
if (players[i].exiting) if (players[i].exiting)
pexiting++; 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 case KSHIELD_NONE:
return 0; /* 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) if (players[i].position == 1)
@ -1359,7 +1374,13 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against)
if (!mobj->player) if (!mobj->player)
return weight; 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 && ((!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 || (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 #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 /** \brief Updates the player's drafting values once per frame
\param player player object passed from K_KartPlayerThink \param player player object passed from K_KartPlayerThink
@ -1961,7 +1994,7 @@ static void K_UpdateDraft(player_t *player)
UINT8 leniency; UINT8 leniency;
UINT8 i; UINT8 i;
if (player->itemtype == KITEM_LIGHTNINGSHIELD) if (K_HasInfiniteTether(player))
{ {
// Lightning Shield gets infinite draft distance as its (other) passive effect. // Lightning Shield gets infinite draft distance as its (other) passive effect.
draftdistance = 0; draftdistance = 0;
@ -2399,7 +2432,7 @@ void K_SpawnDriftBoostClipSpark(mobj_t *clip)
spark->momy = clip->momx/2; 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), 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), player->mo->y + (P_RandomRange(PR_DECORATION,-36,36) * player->mo->scale),
@ -2407,20 +2440,40 @@ void K_SpawnNormalSpeedLines(player_t *player)
MT_FASTLINE); MT_FASTLINE);
P_SetTarget(&fast->target, player->mo); P_SetTarget(&fast->target, player->mo);
P_InitAngle(fast, K_MomentumAngle(player->mo));
fast->momx = 3*player->mo->momx/4; fast->momx = 3*player->mo->momx/4;
fast->momy = 3*player->mo->momy/4; fast->momy = 3*player->mo->momy/4;
fast->momz = 3*P_GetMobjZMovement(player->mo)/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_InitAngle(fast, player->mo->angle);
P_SetScale(fast, 3*fast->scale/2); 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 // Make it red when you have the eggman speed boost
fast->color = SKINCOLOR_RED; 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) void K_SpawnInvincibilitySpeedLines(mobj_t *mo)
{ {
mobj_t *fast = P_SpawnMobjFromMobj(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) 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); mobj_t *fx = P_SpawnMobj(mo->x, mo->y, mo->z, MT_BUMP);
if (mo->eflags & MFE_VERTICALFLIP) if (mo->eflags & MFE_VERTICALFLIP)
fx->eflags |= MFE_VERTICALFLIP; fx->eflags |= MFE_VERTICALFLIP;
else else
fx->eflags &= ~MFE_VERTICALFLIP; fx->eflags &= ~MFE_VERTICALFLIP;
fx->scale = mo->scale; 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) static SINT8 K_GlanceAtPlayers(player_t *glancePlayer)
@ -2708,6 +2778,10 @@ void K_KartMoveAnimation(player_t *player)
drift = intsign(player->aizdriftturn); drift = intsign(player->aizdriftturn);
turndir = 0; turndir = 0;
} }
else if (player->curshield == KSHIELD_TOP)
{
drift = -turndir;
}
else if (turndir == 0 && drift == 0) else if (turndir == 0 && drift == 0)
{ {
// Only try glancing if you're driving straight. // 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) if (player->invincibilitytimer || player->hyudorotimer || player->sneakertimer)
return false; return false;
if (K_IsRidingFloatingTop(player))
return false;
return true; return true;
} }
@ -3218,6 +3294,8 @@ boolean K_SlopeResistance(player_t *player)
{ {
if (player->invincibilitytimer || player->sneakertimer || player->tiregrease || player->flamedash) if (player->invincibilitytimer || player->sneakertimer || player->tiregrease || player->flamedash)
return true; return true;
if (player->curshield == KSHIELD_TOP)
return true;
return false; return false;
} }
@ -3281,6 +3359,11 @@ boolean K_WaterRun(mobj_t *mobj)
return false; return false;
} }
if (mobj->player->curshield == KSHIELD_TOP)
{
return K_IsHoldingDownTop(mobj->player) == false;
}
if (mobj->player->invincibilitytimer if (mobj->player->invincibilitytimer
|| mobj->player->sneakertimer || mobj->player->sneakertimer
|| mobj->player->tiregrease || mobj->player->tiregrease
@ -3311,6 +3394,16 @@ boolean K_WaterSkip(mobj_t *mobj)
switch (mobj->type) switch (mobj->type)
{ {
case MT_PLAYER: case MT_PLAYER:
{
if (mobj->player != NULL && mobj->player->curshield == KSHIELD_TOP)
{
// Don't allow
return false;
}
// Allow
break;
}
case MT_ORBINAUT: case MT_ORBINAUT:
case MT_JAWZ: case MT_JAWZ:
case MT_BALLHOG: 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) static fixed_t K_FlameShieldDashVar(INT32 val)
{ {
// 1 second = 75% + 50% top speed // 1 second = 75% + 50% top speed
@ -3648,7 +3781,7 @@ static void K_GetKartBoostPower(player_t *player)
draftspeed *= 2; draftspeed *= 2;
} }
if (player->itemtype == KITEM_LIGHTNINGSHIELD) if (K_HasInfiniteTether(player))
{ {
// infinite tether // infinite tether
draftspeed *= 2; draftspeed *= 2;
@ -3770,6 +3903,10 @@ fixed_t K_GetKartAccel(player_t *player)
if (gametype == GT_BATTLE && player->bumpers <= 0) if (gametype == GT_BATTLE && player->bumpers <= 0)
k_accel *= 2; 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); return FixedMul(k_accel, (FRACUNIT + player->accelboost) / 4);
} }
@ -3859,17 +3996,35 @@ SINT8 K_GetForwardMove(player_t *player)
forwardmove = MAXPLMOVE; forwardmove = MAXPLMOVE;
} }
if (player->curshield == KSHIELD_TOP)
{
if (forwardmove < 0 ||
(K_GetKartButtons(player) & BT_DRIFT))
{
forwardmove = 0;
}
else
{
forwardmove = MAXPLMOVE;
}
}
return forwardmove; return forwardmove;
} }
fixed_t K_GetNewSpeed(player_t *player) fixed_t K_GetNewSpeed(player_t *player)
{ {
const fixed_t accelmax = 4000; 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 p_accel = K_GetKartAccel(player);
fixed_t newspeed, oldspeed, finalspeed; 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) if (K_PlayerUsesBotMovement(player) == true && player->botvars.rubberband > 0)
{ {
// Acceleration is tied to top speed... // 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) static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, INT32 flags2, fixed_t speed, SINT8 dir)
{ {
mobj_t *th; 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->destscale = th->destscale << 1;
th->scalespeed = abs(th->destscale - th->scale) / (2*TICRATE); th->scalespeed = abs(th->destscale - th->scale) / (2*TICRATE);
break; break;
case MT_GARDENTOP:
th->movefactor = finalspeed;
break;
default: default:
break; break;
} }
@ -5670,8 +5841,11 @@ void K_DriftDustHandling(mobj_t *spawner)
dust->destscale = spawner->scale * 3; dust->destscale = spawner->scale * 3;
dust->scalespeed = spawner->scale/12; dust->scalespeed = spawner->scale/12;
if (leveltime % 6 == 0) if (!spawner->player || !K_GetGardenTop(spawner->player))
S_StartSound(spawner, sfx_screec); {
if (leveltime % 6 == 0)
S_StartSound(spawner, sfx_screec);
}
K_MatchGenericExtraFlags(dust, spawner); K_MatchGenericExtraFlags(dust, spawner);
@ -5831,7 +6005,7 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
if (missile) // Shootables if (missile) // Shootables
{ {
if (dir < 0 && mapthing != MT_SPB) if (dir < 0 && mapthing != MT_SPB && mapthing != MT_GARDENTOP)
{ {
// Shoot backward // Shoot backward
mo = K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + angleOffset, 0, PROJSPEED, dir); 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); flip = P_MobjFlip(player->mo);
ponground = P_IsObjectOnGround(player->mo); ponground = P_IsObjectOnGround(player->mo);
if (shield != KSHIELD_NONE && !keepshields) if (shield != KSHIELD_NONE && shield != KSHIELD_TOP && !keepshields)
{ {
if (shield == KSHIELD_LIGHTNING) if (shield == KSHIELD_LIGHTNING)
{ {
@ -6494,6 +6668,9 @@ void K_DropHnextList(player_t *player, boolean keepshields)
orbit = false; orbit = false;
type = MT_EGGMANITEM; type = MT_EGGMANITEM;
break; break;
case MT_GARDENTOP:
Obj_GardenTopDestroy(player);
return;
// intentionally do nothing // intentionally do nothing
case MT_ROCKETSNEAKER: case MT_ROCKETSNEAKER:
case MT_SINK_SHIELD: case MT_SINK_SHIELD:
@ -7800,6 +7977,33 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
{ {
const boolean onground = P_IsObjectOnGround(player->mo); 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_UpdateOffroad(player);
K_UpdateDraft(player); K_UpdateDraft(player);
K_UpdateEngineSounds(player); // Thanks, VAda! K_UpdateEngineSounds(player); // Thanks, VAda!
@ -8244,8 +8448,20 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (cmd->buttons & BT_DRIFT) 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. // 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; player->pflags |= PF_DRIFTINPUT;
} }
@ -8382,7 +8598,7 @@ void K_KartResetPlayerColor(player_t *player)
finalise: finalise:
if (player->curshield) if (player->curshield && player->curshield != KSHIELD_TOP)
{ {
fullbright = true; fullbright = true;
} }
@ -9027,13 +9243,25 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
if ((currentSpeed <= 0) // Not moving if ((currentSpeed <= 0) // Not moving
&& ((K_GetKartButtons(player) & BT_EBRAKEMASK) != BT_EBRAKEMASK) // Not e-braking && ((K_GetKartButtons(player) & BT_EBRAKEMASK) != BT_EBRAKEMASK) // Not e-braking
&& (player->respawn.state == RESPAWNST_NONE) // Not respawning && (player->respawn.state == RESPAWNST_NONE) // Not respawning
&& (player->curshield != KSHIELD_TOP) // Not riding a Top
&& (P_IsObjectOnGround(player->mo) == true)) // On the ground && (P_IsObjectOnGround(player->mo) == true)) // On the ground
{ {
return 0; return 0;
} }
p_maxspeed = K_GetKartSpeed(player, false, true); 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)); weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT));
if (K_PlayerUsesBotMovement(player)) if (K_PlayerUsesBotMovement(player))
@ -9060,7 +9288,9 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
turnfixed = FixedMul(turnfixed, FRACUNIT + player->handleboost); 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); fixed_t div = min(FRACUNIT + K_GetUnderwaterStrafeMul(player), 2*FRACUNIT);
turnfixed = FixedDiv(turnfixed, div); turnfixed = FixedDiv(turnfixed, div);
@ -9448,6 +9678,7 @@ void K_KartUpdatePosition(player_t *player)
fixed_t position = 1; fixed_t position = 1;
fixed_t oldposition = player->position; fixed_t oldposition = player->position;
fixed_t i; fixed_t i;
INT32 realplayers = 0;
if (player->spectator || !player->mo) if (player->spectator || !player->mo)
{ {
@ -9462,6 +9693,8 @@ void K_KartUpdatePosition(player_t *player)
if (!playeringame[i] || players[i].spectator || !players[i].mo) if (!playeringame[i] || players[i].spectator || !players[i].mo)
continue; continue;
realplayers++;
if (gametyperules & GTR_CIRCUIT) if (gametyperules & GTR_CIRCUIT)
{ {
if (player->exiting) // End of match standings if (player->exiting) // End of match standings
@ -9525,6 +9758,33 @@ void K_KartUpdatePosition(player_t *player)
if (oldposition != position) // Changed places? if (oldposition != position) // Changed places?
player->positiondelay = 10; // Position number growth 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; player->position = position;
} }
@ -9676,6 +9936,7 @@ void K_KartEbrakeVisuals(player_t *p)
p->mo->hprev->angle = p->mo->angle; 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. 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); K_FlipFromObject(p->mo->hprev, p->mo);
p->mo->hprev->sprzoff = p->mo->sprzoff;
} }
if (!p->spindash) if (!p->spindash)
@ -10043,6 +10304,11 @@ void K_AdjustPlayerFriction(player_t *player)
player->mo->friction += ((FRACUNIT - prevfriction) / greasetics) * player->tiregrease; player->mo->friction += ((FRACUNIT - prevfriction) / greasetics) * player->tiregrease;
} }
if (player->curshield == KSHIELD_TOP)
{
player->mo->friction += 1024;
}
/* /*
if (K_PlayerEBrake(player) == true) if (K_PlayerEBrake(player) == true)
{ {
@ -10693,6 +10959,37 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
} }
} }
break; 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: case KITEM_BUBBLESHIELD:
if (player->curshield != KSHIELD_BUBBLE) 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_SpawnDriftBoostClip(player_t *player);
void K_SpawnDriftBoostClipSpark(mobj_t *clip); void K_SpawnDriftBoostClipSpark(mobj_t *clip);
void K_SpawnNormalSpeedLines(player_t *player); void K_SpawnNormalSpeedLines(player_t *player);
void K_SpawnGardenTopSpeedLines(player_t *player);
void K_SpawnInvincibilitySpeedLines(mobj_t *mo); void K_SpawnInvincibilitySpeedLines(mobj_t *mo);
void K_SpawnBumpEffect(mobj_t *mo); void K_SpawnBumpEffect(mobj_t *mo);
void K_KartMoveAnimation(player_t *player); void K_KartMoveAnimation(player_t *player);
@ -146,6 +147,9 @@ boolean K_MovingHorizontally(mobj_t *mobj);
boolean K_WaterRun(mobj_t *mobj); boolean K_WaterRun(mobj_t *mobj);
boolean K_WaterSkip(mobj_t *mobj); boolean K_WaterSkip(mobj_t *mobj);
void K_SpawnWaterRunParticles(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); void K_ApplyTripWire(player_t *player, tripwirestate_t state);
INT16 K_GetSpindashChargeTime(player_t *player); INT16 K_GetSpindashChargeTime(player_t *player);
fixed_t K_GetSpindashChargeSpeed(player_t *player); fixed_t K_GetSpindashChargeSpeed(player_t *player);
@ -172,6 +176,7 @@ UINT8 K_GetOrbinautItemFrame(UINT8 count);
boolean K_IsSPBInGame(void); boolean K_IsSPBInGame(void);
void K_KartEbrakeVisuals(player_t *p); void K_KartEbrakeVisuals(player_t *p);
void K_HandleDirectionalInfluence(player_t *player); void K_HandleDirectionalInfluence(player_t *player);
fixed_t K_DefaultPlayerRadius(player_t *player);
// sound stuff for lua // sound stuff for lua
void K_PlayAttackTaunt(mobj_t *source); 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_HyudoroCenterThink(mobj_t *actor);
void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher); 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 */ /* Shrink */
void Obj_PohbeeThinker(mobj_t *pohbee); void Obj_PohbeeThinker(mobj_t *pohbee);
void Obj_PohbeeRemoved(mobj_t *pohbee); void Obj_PohbeeRemoved(mobj_t *pohbee);

View file

@ -1,4 +1,5 @@
hyudoro.c hyudoro.c
gardentop.c
shrink.c shrink.c
item-debris.c item-debris.c
spb.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 Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
{ {
boolean damageitem = false; boolean damageitem = false;
boolean tumbleitem = false;
boolean sprung = false; boolean sprung = false;
if ((orbinaut_selfdelay(t1) > 0 && t2->hitlag > 0) if ((orbinaut_selfdelay(t1) > 0 && t2->hitlag > 0)
@ -173,6 +174,11 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
return true; return true;
} }
if (t1->type == MT_GARDENTOP)
{
tumbleitem = true;
}
if (t2->player) if (t2->player)
{ {
if ((t2->player->flashing > 0 && t2->hitlag == 0) if ((t2->player->flashing > 0 && t2->hitlag == 0)
@ -190,7 +196,8 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
else else
{ {
// Player Damage // 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); K_KartBouncing(t2, t1);
S_StartSound(t2, sfx_s3k7b); S_StartSound(t2, sfx_s3k7b);
} }
@ -233,6 +240,11 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
damageitem = true; damageitem = true;
} }
if (t1->type == MT_GARDENTOP)
{
damageitem = false;
}
if (damageitem) if (damageitem)
{ {
// This Item Damage // 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; player->emeralds = 0;
K_CheckEmeralds(source->player); K_CheckEmeralds(source->player);
} }
/* Drop "shield" immediately on contact. */
if (source->player->curshield == KSHIELD_TOP)
{
Obj_GardenTopDestroy(source->player);
}
} }
else 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_ORBINAUT || tmthing->type == MT_JAWZ
|| tmthing->type == MT_BANANA || tmthing->type == MT_EGGMANITEM || tmthing->type == MT_BALLHOG || 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_SSMINE || tmthing->type == MT_LANDMINE || tmthing->type == MT_SINK
|| tmthing->type == MT_GARDENTOP
|| (tmthing->type == MT_PLAYER && thing->target != tmthing))) || (tmthing->type == MT_PLAYER && thing->target != tmthing)))
{ {
// see if it went over / under // 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_ORBINAUT || thing->type == MT_JAWZ
|| thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG || 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_SSMINE || tmthing->type == MT_LANDMINE || thing->type == MT_SINK
|| thing->type == MT_GARDENTOP
|| (thing->type == MT_PLAYER && tmthing->target != thing))) || (thing->type == MT_PLAYER && tmthing->target != thing)))
{ {
// see if it went over / under // 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_ORBINAUT || tmthing->type == MT_JAWZ
|| tmthing->type == MT_BANANA || tmthing->type == MT_EGGMANITEM || tmthing->type == MT_BALLHOG || 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_SSMINE || tmthing->type == MT_LANDMINE || tmthing->type == MT_SINK
|| tmthing->type == MT_GARDENTOP
|| (tmthing->type == MT_PLAYER))) || (tmthing->type == MT_PLAYER)))
{ {
// see if it went over / under // 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_ORBINAUT || thing->type == MT_JAWZ
|| thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG || 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_SSMINE || tmthing->type == MT_LANDMINE || thing->type == MT_SINK
|| thing->type == MT_GARDENTOP
|| (thing->type == MT_PLAYER))) || (thing->type == MT_PLAYER)))
{ {
// see if it went over / under // see if it went over / under
@ -932,7 +936,8 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return BMIT_CONTINUE; return BMIT_CONTINUE;
if (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ 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 // see if it went over / under
if (tmthing->z > thing->z + thing->height) 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; return Obj_OrbinautJawzCollide(tmthing, thing) ? BMIT_CONTINUE : BMIT_ABORT;
} }
else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ 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 // see if it went over / under
if (tmthing->z > thing->z + thing->height) 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; thing->terrain = NULL;
} }
if (thing->player && K_IsRidingFloatingTop(thing->player))
{
stairjank = false;
}
/* FIXME: slope step down (even up) has some false /* FIXME: slope step down (even up) has some false
positives, so just ignore them entirely. */ positives, so just ignore them entirely. */
if (stairjank && !oldslope && !thing->standingslope && if (stairjank && !oldslope && !thing->standingslope &&

View file

@ -1133,7 +1133,11 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
gravityadd = FixedMul(TUMBLEGRAVITY, gravityadd); 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 // Fast falling
gravityadd *= 4; gravityadd *= 4;
@ -1734,6 +1738,7 @@ void P_XYMovement(mobj_t *mo)
switch (mo->type) switch (mo->type)
{ {
case MT_ORBINAUT: // Orbinaut speed decreasing case MT_ORBINAUT: // Orbinaut speed decreasing
case MT_GARDENTOP:
if (mo->health > 1) if (mo->health > 1)
{ {
S_StartSound(mo, mo->info->attacksound); S_StartSound(mo, mo->info->attacksound);
@ -6444,6 +6449,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
return false; return false;
} }
/* FALLTHRU */ /* FALLTHRU */
case MT_GARDENTOP:
case MT_ORBINAUT_SHIELD: case MT_ORBINAUT_SHIELD:
case MT_BANANA_SHIELD: case MT_BANANA_SHIELD:
case MT_EGGMANITEM_SHIELD: case MT_EGGMANITEM_SHIELD:
@ -7152,7 +7158,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
} }
} }
break; break;
case MT_TRIPWIREBOOST: case MT_TRIPWIREBOOST: {
mobj_t *top;
fixed_t newHeight;
fixed_t newScale;
if (!mobj->target || !mobj->target->health if (!mobj->target || !mobj->target->health
|| !mobj->target->player || !mobj->target->player->tripwireLeniency) || !mobj->target->player || !mobj->target->player->tripwireLeniency)
{ {
@ -7160,10 +7170,21 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
return false; 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); mobj->angle = K_MomentumAngle(mobj->target);
P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (mobj->target->height >> 1)); P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (newHeight / 2));
mobj->destscale = mobj->target->scale; mobj->destscale = newScale;
P_SetScale(mobj, mobj->target->scale); P_SetScale(mobj, newScale);
if (mobj->extravalue1) if (mobj->extravalue1)
{ {
@ -7235,6 +7256,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
} }
} }
break; break;
}
case MT_BOOSTFLAME: case MT_BOOSTFLAME:
if (!mobj->target || !mobj->target->health) if (!mobj->target || !mobj->target->health)
{ {
@ -7806,6 +7828,16 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
} }
break; break;
} }
case MT_GARDENTOP:
{
Obj_GardenTopThink(mobj);
break;
}
case MT_GARDENTOPSPARK:
{
Obj_GardenTopSparkThink(mobj);
break;
}
case MT_HYUDORO: case MT_HYUDORO:
{ {
Obj_HyudoroThink(mobj); Obj_HyudoroThink(mobj);
@ -9851,6 +9883,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing)
case MT_BUBBLESHIELD: case MT_BUBBLESHIELD:
case MT_BUBBLESHIELDTRAP: case MT_BUBBLESHIELDTRAP:
case MT_FLAMESHIELD: case MT_FLAMESHIELD:
case MT_GARDENTOP:
thing->shadowscale = FRACUNIT; thing->shadowscale = FRACUNIT;
break; break;
case MT_RING: 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].kickstartaccel);
WRITEUINT8(save_p, players[i].stairjank); WRITEUINT8(save_p, players[i].stairjank);
WRITEUINT8(save_p, players[i].topdriftheld);
WRITEUINT8(save_p, players[i].topinfirst);
WRITEUINT8(save_p, players[i].shrinkLaserDelay); WRITEUINT8(save_p, players[i].shrinkLaserDelay);
@ -674,6 +676,8 @@ static void P_NetUnArchivePlayers(void)
players[i].kickstartaccel = READUINT8(save_p); players[i].kickstartaccel = READUINT8(save_p);
players[i].stairjank = 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); 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) switch (special)
{ {
case 1: // Damage (Generic) 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); P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL);
break; break;
case 2: // Damage (Water) // SRB2kart - These three damage types are now offroad sectors 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) case 4: // Damage (Electrical)
break; break;
case 5: // Spikes 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); P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL);
break; break;
case 6: // Death Pit (Camera Mod) 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); 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 // THINKERS
// All thinkers should be allocated by Z_Calloc // 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); INT32 P_AltFlip(INT32 value, tic_t tics);
#define P_RandomFlip(value) P_AltFlip(value, 1) #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 #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 // 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); 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); movepushangle = K_MomentumAngle(player->mo);
} }
@ -1884,6 +1884,7 @@ static void P_3dMovement(player_t *player)
if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration... if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration...
movepushforward = FixedMul(movepushforward, player->mo->movefactor); movepushforward = FixedMul(movepushforward, player->mo->movefactor);
if (player->curshield != KSHIELD_TOP)
{ {
INT32 a = K_GetUnderwaterTurnAdjust(player); INT32 a = K_GetUnderwaterTurnAdjust(player);
INT32 adj = 0; INT32 adj = 0;
@ -1967,6 +1968,24 @@ static void P_3dMovement(player_t *player)
player->mo->momx += totalthrust.x; player->mo->momx += totalthrust.x;
player->mo->momy += totalthrust.y; 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) if (!onground)
{ {
const fixed_t airspeedcap = (50*mapobjectscale); 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) if (!resetcalled && (leveltime >= introtime && timeover != 2)
&& (t_cam_rotate[num] != -42)) && (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; angle_t rollAngle = 0;
mobj_t *top = K_GetGardenTop(player);
if (player->mo->eflags & MFE_UNDERWATER) if (player->mo->eflags & MFE_UNDERWATER)
{ {
rollAngle -= player->underwatertilt; rollAngle -= player->underwatertilt;
@ -61,6 +63,14 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer)
(17 / player->stairjank)); (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; return rollAngle;
} }