mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-12-23 00:12:27 +00:00
Merge branch 'special-stages' into 'master'
Special Stages See merge request KartKrew/Kart!778
This commit is contained in:
commit
3101cf25cd
20 changed files with 1669 additions and 319 deletions
12
src/d_main.c
12
src/d_main.c
|
|
@ -1867,6 +1867,18 @@ void D_SRB2Main(void)
|
||||||
G_SetUsedCheats();
|
G_SetUsedCheats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (grandprixinfo.gp == true && mapheaderinfo[pstartmap-1])
|
||||||
|
{
|
||||||
|
if (mapheaderinfo[pstartmap-1]->typeoflevel & TOL_SPECIAL)
|
||||||
|
{
|
||||||
|
specialStage.active = true;
|
||||||
|
specialStage.encore = grandprixinfo.encore;
|
||||||
|
grandprixinfo.eventmode = GPEVENT_SPECIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_SetUsedCheats();
|
||||||
|
}
|
||||||
|
|
||||||
D_MapChange(pstartmap, gametype, (cv_kartencore.value == 1), true, 0, false, false);
|
D_MapChange(pstartmap, gametype, (cv_kartencore.value == 1), true, 0, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
117
src/d_netcmd.c
117
src/d_netcmd.c
|
|
@ -2853,7 +2853,7 @@ static void Command_Map_f(void)
|
||||||
if (mapheaderinfo[newmapnum-1])
|
if (mapheaderinfo[newmapnum-1])
|
||||||
{
|
{
|
||||||
// Let's just guess so we don't have to specify the gametype EVERY time...
|
// Let's just guess so we don't have to specify the gametype EVERY time...
|
||||||
newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & TOL_RACE) ? GT_RACE : GT_BATTLE;
|
newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & (TOL_BATTLE|TOL_BOSS)) ? GT_BATTLE : GT_RACE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2902,11 +2902,59 @@ static void Command_Map_f(void)
|
||||||
|
|
||||||
if (!(netgame || multiplayer))
|
if (!(netgame || multiplayer))
|
||||||
{
|
{
|
||||||
|
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
|
||||||
|
grandprixinfo.masterbots = false;
|
||||||
|
|
||||||
|
if (option_skill)
|
||||||
|
{
|
||||||
|
const char *skillname = COM_Argv(option_skill + 1);
|
||||||
|
INT32 newskill = -1;
|
||||||
|
INT32 j;
|
||||||
|
|
||||||
|
for (j = 0; gpdifficulty_cons_t[j].strvalue; j++)
|
||||||
|
{
|
||||||
|
if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, skillname))
|
||||||
|
{
|
||||||
|
newskill = (INT16)gpdifficulty_cons_t[j].value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match
|
||||||
|
{
|
||||||
|
INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too
|
||||||
|
if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER)
|
||||||
|
newskill = (INT16)num;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newskill != -1)
|
||||||
|
{
|
||||||
|
if (newskill == KARTGP_MASTER)
|
||||||
|
{
|
||||||
|
grandprixinfo.gamespeed = KARTSPEED_HARD;
|
||||||
|
grandprixinfo.masterbots = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
grandprixinfo.gamespeed = newskill;
|
||||||
|
grandprixinfo.masterbots = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grandprixinfo.encore = newencoremode;
|
||||||
|
|
||||||
|
grandprixinfo.gp = true;
|
||||||
|
grandprixinfo.roundnum = 0;
|
||||||
|
grandprixinfo.cup = NULL;
|
||||||
|
grandprixinfo.wonround = false;
|
||||||
|
grandprixinfo.initalize = true;
|
||||||
|
|
||||||
|
grandprixinfo.eventmode = GPEVENT_NONE;
|
||||||
|
|
||||||
if (newgametype == GT_BATTLE)
|
if (newgametype == GT_BATTLE)
|
||||||
{
|
{
|
||||||
grandprixinfo.gp = false;
|
grandprixinfo.eventmode = GPEVENT_BONUS;
|
||||||
specialStage.active = false;
|
|
||||||
K_ResetBossInfo();
|
|
||||||
|
|
||||||
if (mapheaderinfo[newmapnum-1] &&
|
if (mapheaderinfo[newmapnum-1] &&
|
||||||
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_BOSS)
|
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_BOSS)
|
||||||
|
|
@ -2914,71 +2962,24 @@ static void Command_Map_f(void)
|
||||||
bossinfo.boss = true;
|
bossinfo.boss = true;
|
||||||
bossinfo.encore = newencoremode;
|
bossinfo.encore = newencoremode;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bossinfo.boss = false;
|
||||||
|
K_ResetBossInfo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (mapheaderinfo[newmapnum-1] &&
|
if (mapheaderinfo[newmapnum-1] &&
|
||||||
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage
|
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage
|
||||||
{
|
{
|
||||||
grandprixinfo.gp = false;
|
|
||||||
bossinfo.boss = false;
|
|
||||||
|
|
||||||
specialStage.active = true;
|
specialStage.active = true;
|
||||||
specialStage.encore = newencoremode;
|
specialStage.encore = newencoremode;
|
||||||
|
grandprixinfo.eventmode = GPEVENT_SPECIAL;
|
||||||
}
|
}
|
||||||
else // default GP
|
else
|
||||||
{
|
{
|
||||||
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
|
|
||||||
grandprixinfo.masterbots = false;
|
|
||||||
|
|
||||||
if (option_skill)
|
|
||||||
{
|
|
||||||
const char *skillname = COM_Argv(option_skill + 1);
|
|
||||||
INT32 newskill = -1;
|
|
||||||
INT32 j;
|
|
||||||
|
|
||||||
for (j = 0; gpdifficulty_cons_t[j].strvalue; j++)
|
|
||||||
{
|
|
||||||
if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, skillname))
|
|
||||||
{
|
|
||||||
newskill = (INT16)gpdifficulty_cons_t[j].value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match
|
|
||||||
{
|
|
||||||
INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too
|
|
||||||
if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER)
|
|
||||||
newskill = (INT16)num;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newskill != -1)
|
|
||||||
{
|
|
||||||
if (newskill == KARTGP_MASTER)
|
|
||||||
{
|
|
||||||
grandprixinfo.gamespeed = KARTSPEED_HARD;
|
|
||||||
grandprixinfo.masterbots = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
grandprixinfo.gamespeed = newskill;
|
|
||||||
grandprixinfo.masterbots = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
grandprixinfo.encore = newencoremode;
|
|
||||||
|
|
||||||
grandprixinfo.gp = true;
|
|
||||||
grandprixinfo.roundnum = 0;
|
|
||||||
grandprixinfo.cup = NULL;
|
|
||||||
grandprixinfo.wonround = false;
|
|
||||||
|
|
||||||
bossinfo.boss = false;
|
|
||||||
specialStage.active = false;
|
specialStage.active = false;
|
||||||
|
|
||||||
grandprixinfo.initalize = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4534,6 +4534,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
|
||||||
// Broly Ki Orb
|
// Broly Ki Orb
|
||||||
"S_BROLY1",
|
"S_BROLY1",
|
||||||
"S_BROLY2",
|
"S_BROLY2",
|
||||||
|
|
||||||
|
"S_SPECIAL_UFO_POD",
|
||||||
|
"S_SPECIAL_UFO_OVERLAY",
|
||||||
|
"S_SPECIAL_UFO_ARM",
|
||||||
|
"S_SPECIAL_UFO_STEM",
|
||||||
};
|
};
|
||||||
|
|
||||||
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
|
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
|
||||||
|
|
@ -5631,6 +5636,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
||||||
"MT_BEAMPOINT",
|
"MT_BEAMPOINT",
|
||||||
|
|
||||||
"MT_BROLY",
|
"MT_BROLY",
|
||||||
|
|
||||||
|
"MT_SPECIAL_UFO",
|
||||||
|
"MT_SPECIAL_UFO_PIECE",
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *const MOBJFLAG_LIST[] = {
|
const char *const MOBJFLAG_LIST[] = {
|
||||||
|
|
|
||||||
|
|
@ -549,7 +549,7 @@ void DRPC_UpdatePresence(void)
|
||||||
if (gamestate == GS_LEVEL && Playing())
|
if (gamestate == GS_LEVEL && Playing())
|
||||||
{
|
{
|
||||||
const time_t currentTime = time(NULL);
|
const time_t currentTime = time(NULL);
|
||||||
const time_t mapTimeStart = currentTime - ((leveltime + (modeattacking ? starttime : 0)) / TICRATE);
|
const time_t mapTimeStart = currentTime - ((leveltime + starttime) / TICRATE);
|
||||||
|
|
||||||
discordPresence.startTimestamp = mapTimeStart;
|
discordPresence.startTimestamp = mapTimeStart;
|
||||||
|
|
||||||
|
|
|
||||||
65
src/info.c
65
src/info.c
|
|
@ -785,6 +785,10 @@ char sprnames[NUMSPRITES + 1][5] =
|
||||||
|
|
||||||
"FLBM",
|
"FLBM",
|
||||||
|
|
||||||
|
"UFOB",
|
||||||
|
"UFOA",
|
||||||
|
"UFOS",
|
||||||
|
|
||||||
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
||||||
"VIEW",
|
"VIEW",
|
||||||
};
|
};
|
||||||
|
|
@ -5146,6 +5150,11 @@ state_t states[NUMSTATES] =
|
||||||
// Broly Ki Orb
|
// Broly Ki Orb
|
||||||
{SPR_LSSJ, FF_REVERSESUBTRACT|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_BROLY2}, // S_BROLY1
|
{SPR_LSSJ, FF_REVERSESUBTRACT|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_BROLY2}, // S_BROLY1
|
||||||
{SPR_NULL, 0, 5*TICRATE, {A_SSMineFlash}, 0, 0, S_NULL}, // S_BROLY2
|
{SPR_NULL, 0, 5*TICRATE, {A_SSMineFlash}, 0, 0, S_NULL}, // S_BROLY2
|
||||||
|
|
||||||
|
{SPR_UFOB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_POD
|
||||||
|
{SPR_UFOB, 1|FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 1, 1, S_NULL}, // S_SPECIAL_UFO_OVERLAY
|
||||||
|
{SPR_UFOA, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_ARM
|
||||||
|
{SPR_UFOS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_STEM
|
||||||
};
|
};
|
||||||
|
|
||||||
mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
||||||
|
|
@ -23622,7 +23631,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
||||||
100, // mass
|
100, // mass
|
||||||
1, // damage
|
1, // damage
|
||||||
sfx_kc64, // activesound
|
sfx_kc64, // activesound
|
||||||
MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
|
MF_SOLID|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
|
||||||
S_NULL // raisestate
|
S_NULL // raisestate
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -29084,6 +29093,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
||||||
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
|
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
|
||||||
S_NULL // raisestate
|
S_NULL // raisestate
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{ // MT_SPECIAL_UFO
|
||||||
|
-1, // doomednum
|
||||||
|
S_CHAOSEMERALD1, // spawnstate
|
||||||
|
101, // spawnhealth
|
||||||
|
S_NULL, // seestate
|
||||||
|
sfx_None, // seesound
|
||||||
|
8, // reactiontime
|
||||||
|
sfx_None, // attacksound
|
||||||
|
S_NULL, // painstate
|
||||||
|
0, // painchance
|
||||||
|
sfx_None, // painsound
|
||||||
|
S_NULL, // meleestate
|
||||||
|
S_NULL, // missilestate
|
||||||
|
S_NULL, // deathstate
|
||||||
|
S_NULL, // xdeathstate
|
||||||
|
sfx_None, // deathsound
|
||||||
|
0, // speed
|
||||||
|
108*FRACUNIT, // radius
|
||||||
|
72*FRACUNIT, // height
|
||||||
|
0, // display offset
|
||||||
|
16, // mass
|
||||||
|
0, // damage
|
||||||
|
sfx_None, // activesound
|
||||||
|
MF_SHOOTABLE|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
|
||||||
|
S_NULL // raisestate
|
||||||
|
},
|
||||||
|
|
||||||
|
{ // MT_SPECIAL_UFO_PIECE
|
||||||
|
-1, // doomednum
|
||||||
|
S_INVISIBLE, // spawnstate
|
||||||
|
1000, // spawnhealth
|
||||||
|
S_NULL, // seestate
|
||||||
|
sfx_None, // seesound
|
||||||
|
8, // reactiontime
|
||||||
|
sfx_None, // attacksound
|
||||||
|
S_NULL, // painstate
|
||||||
|
0, // painchance
|
||||||
|
sfx_None, // painsound
|
||||||
|
S_NULL, // meleestate
|
||||||
|
S_NULL, // missilestate
|
||||||
|
S_NULL, // deathstate
|
||||||
|
S_NULL, // xdeathstate
|
||||||
|
sfx_None, // deathsound
|
||||||
|
0, // speed
|
||||||
|
8*FRACUNIT, // radius
|
||||||
|
16*FRACUNIT, // height
|
||||||
|
1, // display offset
|
||||||
|
100, // mass
|
||||||
|
1, // damage
|
||||||
|
sfx_None, // activesound
|
||||||
|
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_NOSQUISH, // flags
|
||||||
|
S_NULL // raisestate
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
skincolor_t skincolors[MAXSKINCOLORS] = {
|
skincolor_t skincolors[MAXSKINCOLORS] = {
|
||||||
|
|
|
||||||
12
src/info.h
12
src/info.h
|
|
@ -1332,6 +1332,10 @@ typedef enum sprite
|
||||||
|
|
||||||
SPR_FLBM, // Finish line beam
|
SPR_FLBM, // Finish line beam
|
||||||
|
|
||||||
|
SPR_UFOB,
|
||||||
|
SPR_UFOA,
|
||||||
|
SPR_UFOS,
|
||||||
|
|
||||||
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
||||||
SPR_VIEW,
|
SPR_VIEW,
|
||||||
|
|
||||||
|
|
@ -5569,6 +5573,11 @@ typedef enum state
|
||||||
S_BROLY1,
|
S_BROLY1,
|
||||||
S_BROLY2,
|
S_BROLY2,
|
||||||
|
|
||||||
|
S_SPECIAL_UFO_POD,
|
||||||
|
S_SPECIAL_UFO_OVERLAY,
|
||||||
|
S_SPECIAL_UFO_ARM,
|
||||||
|
S_SPECIAL_UFO_STEM,
|
||||||
|
|
||||||
S_FIRSTFREESLOT,
|
S_FIRSTFREESLOT,
|
||||||
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
|
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
|
||||||
NUMSTATES
|
NUMSTATES
|
||||||
|
|
@ -6686,6 +6695,9 @@ typedef enum mobj_type
|
||||||
|
|
||||||
MT_BROLY,
|
MT_BROLY,
|
||||||
|
|
||||||
|
MT_SPECIAL_UFO,
|
||||||
|
MT_SPECIAL_UFO_PIECE,
|
||||||
|
|
||||||
MT_FIRSTFREESLOT,
|
MT_FIRSTFREESLOT,
|
||||||
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
|
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
|
||||||
NUMMOBJTYPES
|
NUMMOBJTYPES
|
||||||
|
|
|
||||||
374
src/k_kart.c
374
src/k_kart.c
|
|
@ -184,6 +184,9 @@ UINT32 K_GetPlayerDontDrawFlag(player_t *player)
|
||||||
{
|
{
|
||||||
UINT32 flag = 0;
|
UINT32 flag = 0;
|
||||||
|
|
||||||
|
if (player == NULL)
|
||||||
|
return flag;
|
||||||
|
|
||||||
if (player == &players[displayplayers[0]])
|
if (player == &players[displayplayers[0]])
|
||||||
flag = RF_DONTDRAWP1;
|
flag = RF_DONTDRAWP1;
|
||||||
else if (r_splitscreen >= 1 && player == &players[displayplayers[1]])
|
else if (r_splitscreen >= 1 && player == &players[displayplayers[1]])
|
||||||
|
|
@ -507,6 +510,51 @@ void K_RunItemCooldowns(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean K_TimeAttackRules(void)
|
||||||
|
{
|
||||||
|
UINT8 playing = 0;
|
||||||
|
UINT8 i;
|
||||||
|
|
||||||
|
if (specialStage.active == true)
|
||||||
|
{
|
||||||
|
// Kind of a hack -- Special Stages
|
||||||
|
// are expected to be 1-player, so
|
||||||
|
// we won't use the Time Attack changes
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modeattacking != ATTACKING_NONE)
|
||||||
|
{
|
||||||
|
// Time Attack obviously uses Time Attack rules :p
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (battlecapsules == true)
|
||||||
|
{
|
||||||
|
// Break the Capsules always uses Time Attack
|
||||||
|
// rules, since you can bring 2-4 players in
|
||||||
|
// via Grand Prix.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
|
{
|
||||||
|
if (playeringame[i] == false || players[i].spectator == true)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
playing++;
|
||||||
|
if (playing > 1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Time Attack gameplay rules with only 1P.
|
||||||
|
return (playing <= 1);
|
||||||
|
}
|
||||||
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//{ SRB2kart p_user.c Stuff
|
//{ SRB2kart p_user.c Stuff
|
||||||
|
|
@ -530,6 +578,10 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against)
|
||||||
{
|
{
|
||||||
weight = 0; // This player does not cause any bump action
|
weight = 0; // This player does not cause any bump action
|
||||||
}
|
}
|
||||||
|
else if (against && against->type == MT_SPECIAL_UFO)
|
||||||
|
{
|
||||||
|
weight = 0;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Applies rubberbanding, to prevent rubberbanding bots
|
// Applies rubberbanding, to prevent rubberbanding bots
|
||||||
|
|
@ -1029,7 +1081,7 @@ static void K_UpdateOffroad(player_t *player)
|
||||||
player->offroad = 0;
|
player->offroad = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t curdist, fixed_t maxdist, boolean transparent)
|
static void K_DrawDraftCombiring(player_t *player, mobj_t *victim, fixed_t curdist, fixed_t maxdist, boolean transparent)
|
||||||
{
|
{
|
||||||
#define CHAOTIXBANDLEN 15
|
#define CHAOTIXBANDLEN 15
|
||||||
#define CHAOTIXBANDCOLORS 9
|
#define CHAOTIXBANDCOLORS 9
|
||||||
|
|
@ -1060,9 +1112,9 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur
|
||||||
c = FixedMul(CHAOTIXBANDCOLORS<<FRACBITS, FixedDiv(curdist-minimumdist, maxdist-minimumdist)) >> FRACBITS;
|
c = FixedMul(CHAOTIXBANDCOLORS<<FRACBITS, FixedDiv(curdist-minimumdist, maxdist-minimumdist)) >> FRACBITS;
|
||||||
}
|
}
|
||||||
|
|
||||||
stepx = (victim->mo->x - player->mo->x) / CHAOTIXBANDLEN;
|
stepx = (victim->x - player->mo->x) / CHAOTIXBANDLEN;
|
||||||
stepy = (victim->mo->y - player->mo->y) / CHAOTIXBANDLEN;
|
stepy = (victim->y - player->mo->y) / CHAOTIXBANDLEN;
|
||||||
stepz = ((victim->mo->z + (victim->mo->height / 2)) - (player->mo->z + (player->mo->height / 2))) / CHAOTIXBANDLEN;
|
stepz = ((victim->z + (victim->height / 2)) - (player->mo->z + (player->mo->height / 2))) / CHAOTIXBANDLEN;
|
||||||
|
|
||||||
curx = player->mo->x + stepx;
|
curx = player->mo->x + stepx;
|
||||||
cury = player->mo->y + stepy;
|
cury = player->mo->y + stepy;
|
||||||
|
|
@ -1096,7 +1148,7 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur
|
||||||
if (transparent)
|
if (transparent)
|
||||||
band->renderflags |= RF_GHOSTLY;
|
band->renderflags |= RF_GHOSTLY;
|
||||||
|
|
||||||
band->renderflags |= RF_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim));
|
band->renderflags |= RF_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim->player));
|
||||||
}
|
}
|
||||||
|
|
||||||
curx += stepx;
|
curx += stepx;
|
||||||
|
|
@ -1121,6 +1173,129 @@ static boolean K_HasInfiniteTether(player_t *player)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed_t draftdistance, UINT8 leniency)
|
||||||
|
{
|
||||||
|
//#define EASYDRAFTTEST
|
||||||
|
fixed_t dist, olddraft;
|
||||||
|
fixed_t theirSpeed = 0;
|
||||||
|
#ifndef EASYDRAFTTEST
|
||||||
|
angle_t yourangle, theirangle, diff;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EASYDRAFTTEST
|
||||||
|
// Don't draft on yourself :V
|
||||||
|
if (dest->player && dest->player == player)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (dest->player != NULL)
|
||||||
|
{
|
||||||
|
// No tethering off of the guy who got the starting bonus :P
|
||||||
|
if (dest->player->startboost > 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
theirSpeed = dest->player->speed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
theirSpeed = R_PointToDist2(0, 0, dest->momx, dest->momy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// They're not enough speed to draft off of them.
|
||||||
|
if (theirSpeed < 20 * dest->scale)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef EASYDRAFTTEST
|
||||||
|
yourangle = K_MomentumAngle(player->mo);
|
||||||
|
theirangle = K_MomentumAngle(dest);
|
||||||
|
|
||||||
|
// Not in front of this player.
|
||||||
|
diff = AngleDelta(R_PointToAngle2(player->mo->x, player->mo->y, dest->x, dest->y), yourangle);
|
||||||
|
if (diff > ANG10)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not moving in the same direction.
|
||||||
|
diff = AngleDelta(yourangle, theirangle);
|
||||||
|
if (diff > ANGLE_90)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
dist = P_AproxDistance(P_AproxDistance(dest->x - player->mo->x, dest->y - player->mo->y), dest->z - player->mo->z);
|
||||||
|
|
||||||
|
#ifndef EASYDRAFTTEST
|
||||||
|
// TOO close to draft.
|
||||||
|
if (dist < minDist)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not close enough to draft.
|
||||||
|
if (dist > draftdistance && draftdistance > 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
olddraft = player->draftpower;
|
||||||
|
|
||||||
|
player->draftleeway = leniency;
|
||||||
|
|
||||||
|
if (dest->player != NULL)
|
||||||
|
{
|
||||||
|
player->lastdraft = dest->player - players;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
player->lastdraft = MAXPLAYERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draft power is used later in K_GetKartBoostPower, ranging from 0 for normal speed and FRACUNIT for max draft speed.
|
||||||
|
// How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic)
|
||||||
|
if (player->draftpower < FRACUNIT)
|
||||||
|
{
|
||||||
|
fixed_t add = (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600));;
|
||||||
|
player->draftpower += add;
|
||||||
|
|
||||||
|
if (player->bot && player->botvars.rival)
|
||||||
|
{
|
||||||
|
// Double speed for the rival!
|
||||||
|
player->draftpower += add;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gametype == GT_BATTLE)
|
||||||
|
{
|
||||||
|
// TODO: gametyperules
|
||||||
|
// Double speed in Battle
|
||||||
|
player->draftpower += add;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player->draftpower > FRACUNIT)
|
||||||
|
{
|
||||||
|
player->draftpower = FRACUNIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play draft finish noise
|
||||||
|
if (olddraft < FRACUNIT && player->draftpower >= FRACUNIT)
|
||||||
|
{
|
||||||
|
S_StartSound(player->mo, sfx_cdfm62);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn in the visual!
|
||||||
|
K_DrawDraftCombiring(player, dest, dist, draftdistance, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** \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
|
||||||
|
|
@ -1129,6 +1304,9 @@ static boolean K_HasInfiniteTether(player_t *player)
|
||||||
*/
|
*/
|
||||||
static void K_UpdateDraft(player_t *player)
|
static void K_UpdateDraft(player_t *player)
|
||||||
{
|
{
|
||||||
|
const boolean addUfo = ((specialStage.active == true)
|
||||||
|
&& (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false));
|
||||||
|
|
||||||
fixed_t topspd = K_GetKartSpeed(player, false, false);
|
fixed_t topspd = K_GetKartSpeed(player, false, false);
|
||||||
fixed_t draftdistance;
|
fixed_t draftdistance;
|
||||||
fixed_t minDist;
|
fixed_t minDist;
|
||||||
|
|
@ -1166,104 +1344,43 @@ static void K_UpdateDraft(player_t *player)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not enough speed to draft.
|
// Not enough speed to draft.
|
||||||
if (player->speed >= 20*player->mo->scale)
|
if (player->speed >= 20 * player->mo->scale)
|
||||||
{
|
{
|
||||||
//#define EASYDRAFTTEST
|
if (addUfo == true)
|
||||||
|
{
|
||||||
|
// Tether off of the UFO!
|
||||||
|
if (K_TryDraft(player, specialStage.ufo, minDist, draftdistance, leniency) == true)
|
||||||
|
{
|
||||||
|
return; // Finished doing our draft.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Let's hunt for players to draft off of!
|
// Let's hunt for players to draft off of!
|
||||||
for (i = 0; i < MAXPLAYERS; i++)
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
{
|
{
|
||||||
fixed_t dist, olddraft;
|
player_t *otherPlayer = NULL;
|
||||||
#ifndef EASYDRAFTTEST
|
|
||||||
angle_t yourangle, theirangle, diff;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!playeringame[i] || players[i].spectator || !players[i].mo)
|
if (playeringame[i] == false)
|
||||||
continue;
|
|
||||||
|
|
||||||
#ifndef EASYDRAFTTEST
|
|
||||||
// Don't draft on yourself :V
|
|
||||||
if (&players[i] == player)
|
|
||||||
continue;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Not enough speed to draft off of.
|
|
||||||
if (players[i].speed < 20*players[i].mo->scale)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// No tethering off of the guy who got the starting bonus :P
|
|
||||||
if (players[i].startboost > 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
#ifndef EASYDRAFTTEST
|
|
||||||
yourangle = K_MomentumAngle(player->mo);
|
|
||||||
theirangle = K_MomentumAngle(players[i].mo);
|
|
||||||
|
|
||||||
diff = R_PointToAngle2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y) - yourangle;
|
|
||||||
if (diff > ANGLE_180)
|
|
||||||
diff = InvAngle(diff);
|
|
||||||
|
|
||||||
// Not in front of this player.
|
|
||||||
if (diff > ANG10)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
diff = yourangle - theirangle;
|
|
||||||
if (diff > ANGLE_180)
|
|
||||||
diff = InvAngle(diff);
|
|
||||||
|
|
||||||
// Not moving in the same direction.
|
|
||||||
if (diff > ANGLE_90)
|
|
||||||
continue;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
dist = P_AproxDistance(P_AproxDistance(players[i].mo->x - player->mo->x, players[i].mo->y - player->mo->y), players[i].mo->z - player->mo->z);
|
|
||||||
|
|
||||||
#ifndef EASYDRAFTTEST
|
|
||||||
// TOO close to draft.
|
|
||||||
if (dist < minDist)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Not close enough to draft.
|
|
||||||
if (dist > draftdistance && draftdistance > 0)
|
|
||||||
continue;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
olddraft = player->draftpower;
|
|
||||||
|
|
||||||
player->draftleeway = leniency;
|
|
||||||
player->lastdraft = i;
|
|
||||||
|
|
||||||
// Draft power is used later in K_GetKartBoostPower, ranging from 0 for normal speed and FRACUNIT for max draft speed.
|
|
||||||
// How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic)
|
|
||||||
if (player->draftpower < FRACUNIT)
|
|
||||||
{
|
{
|
||||||
fixed_t add = (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600));;
|
continue;
|
||||||
player->draftpower += add;
|
|
||||||
|
|
||||||
if (player->bot && player->botvars.rival)
|
|
||||||
{
|
|
||||||
// Double speed for the rival!
|
|
||||||
player->draftpower += add;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gametype == GT_BATTLE)
|
|
||||||
{
|
|
||||||
// TODO: gametyperules
|
|
||||||
// Double speed in Battle
|
|
||||||
player->draftpower += add;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player->draftpower > FRACUNIT)
|
otherPlayer = &players[i];
|
||||||
player->draftpower = FRACUNIT;
|
|
||||||
|
|
||||||
// Play draft finish noise
|
if (otherPlayer->spectator == true)
|
||||||
if (olddraft < FRACUNIT && player->draftpower >= FRACUNIT)
|
{
|
||||||
S_StartSound(player->mo, sfx_cdfm62);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Spawn in the visual!
|
if (otherPlayer->mo == NULL || P_MobjWasRemoved(otherPlayer->mo) == true)
|
||||||
K_DrawDraftCombiring(player, &players[i], dist, draftdistance, false);
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
return; // Finished doing our draft.
|
if (K_TryDraft(player, otherPlayer->mo, minDist, draftdistance, leniency) == true)
|
||||||
|
{
|
||||||
|
return; // Finished doing our draft.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1284,7 +1401,13 @@ static void K_UpdateDraft(player_t *player)
|
||||||
{
|
{
|
||||||
player_t *victim = &players[player->lastdraft];
|
player_t *victim = &players[player->lastdraft];
|
||||||
fixed_t dist = P_AproxDistance(P_AproxDistance(victim->mo->x - player->mo->x, victim->mo->y - player->mo->y), victim->mo->z - player->mo->z);
|
fixed_t dist = P_AproxDistance(P_AproxDistance(victim->mo->x - player->mo->x, victim->mo->y - player->mo->y), victim->mo->z - player->mo->z);
|
||||||
K_DrawDraftCombiring(player, victim, dist, draftdistance, true);
|
K_DrawDraftCombiring(player, victim->mo, dist, draftdistance, true);
|
||||||
|
}
|
||||||
|
else if (addUfo == true)
|
||||||
|
{
|
||||||
|
// kind of a hack to not have to mess with how lastdraft works
|
||||||
|
fixed_t dist = P_AproxDistance(P_AproxDistance(specialStage.ufo->x - player->mo->x, specialStage.ufo->y - player->mo->y), specialStage.ufo->z - player->mo->z);
|
||||||
|
K_DrawDraftCombiring(player, specialStage.ufo, dist, draftdistance, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // Remove draft speed boost.
|
else // Remove draft speed boost.
|
||||||
|
|
@ -3135,7 +3258,7 @@ boolean K_PlayerShrinkCheat(player_t *player)
|
||||||
return (
|
return (
|
||||||
(player->pflags & PF_SHRINKACTIVE)
|
(player->pflags & PF_SHRINKACTIVE)
|
||||||
&& (player->bot == false)
|
&& (player->bot == false)
|
||||||
&& (modeattacking == false) // Anyone want to make another record attack category?
|
&& (modeattacking == ATTACKING_NONE) // Anyone want to make another record attack category?
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6664,12 +6787,18 @@ static void K_MoveHeldObjects(player_t *player)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
||||||
{
|
{
|
||||||
fixed_t best = INT32_MAX;
|
fixed_t best = INT32_MAX;
|
||||||
player_t *wtarg = NULL;
|
mobj_t *wtarg = NULL;
|
||||||
INT32 i;
|
INT32 i;
|
||||||
|
|
||||||
|
if (specialStage.active == true)
|
||||||
|
{
|
||||||
|
// Always target the UFO.
|
||||||
|
return specialStage.ufo;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < MAXPLAYERS; i++)
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
{
|
{
|
||||||
angle_t thisang = ANGLE_MAX;
|
angle_t thisang = ANGLE_MAX;
|
||||||
|
|
@ -6685,7 +6814,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
||||||
player = &players[i];
|
player = &players[i];
|
||||||
|
|
||||||
// Don't target yourself, stupid.
|
// Don't target yourself, stupid.
|
||||||
if (player == source)
|
if (source != NULL && player == source)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -6724,7 +6853,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
||||||
|
|
||||||
if (gametyperules & GTR_CIRCUIT)
|
if (gametyperules & GTR_CIRCUIT)
|
||||||
{
|
{
|
||||||
if (player->position >= source->position)
|
if (source != NULL && player->position >= source->position)
|
||||||
{
|
{
|
||||||
// Don't pay attention to people who aren't above your position
|
// Don't pay attention to people who aren't above your position
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -6766,7 +6895,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
||||||
|
|
||||||
if (thisScore < best)
|
if (thisScore < best)
|
||||||
{
|
{
|
||||||
wtarg = player;
|
wtarg = player->mo;
|
||||||
best = thisScore;
|
best = thisScore;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7895,24 +8024,32 @@ void K_KartPlayerAfterThink(player_t *player)
|
||||||
// Jawz reticule (seeking)
|
// Jawz reticule (seeking)
|
||||||
if (player->itemtype == KITEM_JAWZ && (player->pflags & PF_ITEMOUT))
|
if (player->itemtype == KITEM_JAWZ && (player->pflags & PF_ITEMOUT))
|
||||||
{
|
{
|
||||||
INT32 lastTargID = player->lastjawztarget;
|
const INT32 lastTargID = player->lastjawztarget;
|
||||||
player_t *lastTarg = NULL;
|
mobj_t *lastTarg = NULL;
|
||||||
player_t *targ = NULL;
|
|
||||||
|
INT32 targID = MAXPLAYERS;
|
||||||
|
mobj_t *targ = NULL;
|
||||||
|
|
||||||
mobj_t *ret = NULL;
|
mobj_t *ret = NULL;
|
||||||
|
|
||||||
if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS)
|
if (specialStage.active == true && lastTargID == MAXPLAYERS)
|
||||||
|
{
|
||||||
|
// Aiming at the UFO.
|
||||||
|
lastTarg = specialStage.ufo;
|
||||||
|
}
|
||||||
|
else if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS)
|
||||||
&& playeringame[lastTargID] == true)
|
&& playeringame[lastTargID] == true)
|
||||||
{
|
{
|
||||||
if (players[lastTargID].spectator == false)
|
if (players[lastTargID].spectator == false)
|
||||||
{
|
{
|
||||||
lastTarg = &players[lastTargID];
|
lastTarg = players[lastTargID].mo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player->throwdir == -1)
|
if (player->throwdir == -1)
|
||||||
{
|
{
|
||||||
// Backwards Jawz targets yourself.
|
// Backwards Jawz targets yourself.
|
||||||
targ = player;
|
targ = player->mo;
|
||||||
player->jawztargetdelay = 0;
|
player->jawztargetdelay = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -7921,9 +8058,14 @@ void K_KartPlayerAfterThink(player_t *player)
|
||||||
targ = K_FindJawzTarget(player->mo, player, ANGLE_45);
|
targ = K_FindJawzTarget(player->mo, player, ANGLE_45);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targ != NULL && targ->mo != NULL && P_MobjWasRemoved(targ->mo) == false)
|
if (targ != NULL && P_MobjWasRemoved(targ) == false)
|
||||||
{
|
{
|
||||||
if (targ - players == lastTargID)
|
if (targ->player != NULL)
|
||||||
|
{
|
||||||
|
targID = targ->player - players;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targID == lastTargID)
|
||||||
{
|
{
|
||||||
// Increment delay.
|
// Increment delay.
|
||||||
if (player->jawztargetdelay < 10)
|
if (player->jawztargetdelay < 10)
|
||||||
|
|
@ -7942,33 +8084,33 @@ void K_KartPlayerAfterThink(player_t *player)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Allow a swap.
|
// Allow a swap.
|
||||||
if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ))
|
if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ->player))
|
||||||
{
|
{
|
||||||
S_StartSound(NULL, sfx_s3k89);
|
S_StartSound(NULL, sfx_s3k89);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
S_StartSound(targ->mo, sfx_s3k89);
|
S_StartSound(targ, sfx_s3k89);
|
||||||
}
|
}
|
||||||
|
|
||||||
player->lastjawztarget = targ - players;
|
player->lastjawztarget = targID;
|
||||||
player->jawztargetdelay = 5;
|
player->jawztargetdelay = 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targ == NULL || targ->mo == NULL || P_MobjWasRemoved(targ->mo) == true)
|
if (targ == NULL || P_MobjWasRemoved(targ) == true)
|
||||||
{
|
{
|
||||||
player->lastjawztarget = -1;
|
player->lastjawztarget = -1;
|
||||||
player->jawztargetdelay = 0;
|
player->jawztargetdelay = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE);
|
ret = P_SpawnMobj(targ->x, targ->y, targ->z, MT_PLAYERRETICULE);
|
||||||
ret->old_x = targ->mo->old_x;
|
ret->old_x = targ->old_x;
|
||||||
ret->old_y = targ->mo->old_y;
|
ret->old_y = targ->old_y;
|
||||||
ret->old_z = targ->mo->old_z;
|
ret->old_z = targ->old_z;
|
||||||
P_SetTarget(&ret->target, targ->mo);
|
P_SetTarget(&ret->target, targ);
|
||||||
ret->frame |= ((leveltime % 10) / 2);
|
ret->frame |= ((leveltime % 10) / 2);
|
||||||
ret->tics = 1;
|
ret->tics = 1;
|
||||||
ret->color = player->skincolor;
|
ret->color = player->skincolor;
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,9 @@ UINT8 K_ItemResultToAmount(SINT8 getitem);
|
||||||
tic_t K_GetItemCooldown(SINT8 itemResult);
|
tic_t K_GetItemCooldown(SINT8 itemResult);
|
||||||
void K_SetItemCooldown(SINT8 itemResult, tic_t time);
|
void K_SetItemCooldown(SINT8 itemResult, tic_t time);
|
||||||
void K_RunItemCooldowns(void);
|
void K_RunItemCooldowns(void);
|
||||||
|
|
||||||
|
boolean K_TimeAttackRules(void);
|
||||||
|
|
||||||
fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against);
|
fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against);
|
||||||
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2);
|
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2);
|
||||||
boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj);
|
boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj);
|
||||||
|
|
@ -118,7 +121,7 @@ void K_UpdateHnextList(player_t *player, boolean clean);
|
||||||
void K_DropHnextList(player_t *player, boolean keepshields);
|
void K_DropHnextList(player_t *player, boolean keepshields);
|
||||||
void K_RepairOrbitChain(mobj_t *orbit);
|
void K_RepairOrbitChain(mobj_t *orbit);
|
||||||
void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player);
|
void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player);
|
||||||
player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range);
|
mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range);
|
||||||
INT32 K_GetKartRingPower(player_t *player, boolean boosted);
|
INT32 K_GetKartRingPower(player_t *player, boolean boosted);
|
||||||
void K_UpdateDistanceFromFinishLine(player_t *const player);
|
void K_UpdateDistanceFromFinishLine(player_t *const player);
|
||||||
boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y);
|
boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y);
|
||||||
|
|
|
||||||
|
|
@ -58,4 +58,15 @@ void Obj_DuelBombInit(mobj_t *bomb);
|
||||||
mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration);
|
mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration);
|
||||||
void Obj_BrolyKiThink(mobj_t *ki);
|
void Obj_BrolyKiThink(mobj_t *ki);
|
||||||
|
|
||||||
|
/* Special Stage UFO */
|
||||||
|
waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo);
|
||||||
|
void Obj_SpecialUFOThinker(mobj_t *ufo);
|
||||||
|
boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType);
|
||||||
|
void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other);
|
||||||
|
void Obj_UFOPieceThink(mobj_t *piece);
|
||||||
|
void Obj_UFOPieceDead(mobj_t *piece);
|
||||||
|
void Obj_UFOPieceRemoved(mobj_t *piece);
|
||||||
|
mobj_t *Obj_CreateSpecialUFO(void);
|
||||||
|
UINT32 K_GetSpecialUFODistance(void);
|
||||||
|
|
||||||
#endif/*k_objects_H*/
|
#endif/*k_objects_H*/
|
||||||
|
|
|
||||||
150
src/k_roulette.c
150
src/k_roulette.c
|
|
@ -110,7 +110,6 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
|
||||||
|
|
||||||
static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] =
|
static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] =
|
||||||
{
|
{
|
||||||
//K L
|
|
||||||
{ 2, 1 }, // Sneaker
|
{ 2, 1 }, // Sneaker
|
||||||
{ 0, 0 }, // Rocket Sneaker
|
{ 0, 0 }, // Rocket Sneaker
|
||||||
{ 4, 1 }, // Invincibility
|
{ 4, 1 }, // Invincibility
|
||||||
|
|
@ -143,6 +142,40 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] =
|
||||||
{ 0, 0 } // Gachabom x3
|
{ 0, 0 } // Gachabom x3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] =
|
||||||
|
{
|
||||||
|
{ 1, 1, 0, 0 }, // Sneaker
|
||||||
|
{ 0, 0, 0, 0 }, // Rocket Sneaker
|
||||||
|
{ 0, 0, 0, 0 }, // Invincibility
|
||||||
|
{ 0, 0, 0, 0 }, // Banana
|
||||||
|
{ 0, 0, 0, 0 }, // Eggman Monitor
|
||||||
|
{ 1, 1, 0, 0 }, // Orbinaut
|
||||||
|
{ 1, 1, 0, 0 }, // Jawz
|
||||||
|
{ 0, 0, 0, 0 }, // Mine
|
||||||
|
{ 0, 0, 0, 0 }, // Land Mine
|
||||||
|
{ 0, 0, 0, 0 }, // Ballhog
|
||||||
|
{ 0, 0, 0, 1 }, // Self-Propelled Bomb
|
||||||
|
{ 0, 0, 0, 0 }, // Grow
|
||||||
|
{ 0, 0, 0, 0 }, // Shrink
|
||||||
|
{ 0, 0, 0, 0 }, // Lightning Shield
|
||||||
|
{ 0, 0, 0, 0 }, // Bubble Shield
|
||||||
|
{ 0, 0, 0, 0 }, // Flame Shield
|
||||||
|
{ 0, 0, 0, 0 }, // Hyudoro
|
||||||
|
{ 0, 0, 0, 0 }, // Pogo Spring
|
||||||
|
{ 0, 0, 0, 0 }, // Super Ring
|
||||||
|
{ 0, 0, 0, 0 }, // Kitchen Sink
|
||||||
|
{ 0, 0, 0, 0 }, // Drop Target
|
||||||
|
{ 0, 0, 0, 0 }, // Garden Top
|
||||||
|
{ 0, 0, 0, 0 }, // Gachabom
|
||||||
|
{ 0, 1, 1, 0 }, // Sneaker x2
|
||||||
|
{ 0, 0, 1, 1 }, // Sneaker x3
|
||||||
|
{ 0, 0, 0, 0 }, // Banana x3
|
||||||
|
{ 0, 1, 1, 0 }, // Orbinaut x3
|
||||||
|
{ 0, 0, 1, 1 }, // Orbinaut x4
|
||||||
|
{ 0, 0, 1, 1 }, // Jawz x2
|
||||||
|
{ 0, 0, 0, 0 } // Gachabom x3
|
||||||
|
};
|
||||||
|
|
||||||
static kartitems_t K_KartItemReelTimeAttack[] =
|
static kartitems_t K_KartItemReelTimeAttack[] =
|
||||||
{
|
{
|
||||||
KITEM_SNEAKER,
|
KITEM_SNEAKER,
|
||||||
|
|
@ -326,7 +359,6 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (specialStage.active == true)
|
if (specialStage.active == true)
|
||||||
{
|
{
|
||||||
UINT32 ufoDis = K_GetSpecialUFODistance();
|
UINT32 ufoDis = K_GetSpecialUFODistance();
|
||||||
|
|
@ -343,7 +375,6 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
UINT8 i;
|
UINT8 i;
|
||||||
for (i = 0; i < MAXPLAYERS; i++)
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
|
|
@ -475,6 +506,11 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
|
||||||
I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table
|
I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table
|
||||||
newOdds = K_KartItemOddsBattle[item-1][pos];
|
newOdds = K_KartItemOddsBattle[item-1][pos];
|
||||||
}
|
}
|
||||||
|
else if (specialStage.active == true)
|
||||||
|
{
|
||||||
|
I_Assert(pos < 4); // Ditto
|
||||||
|
newOdds = K_KartItemOddsSpecial[item-1][pos];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
I_Assert(pos < 8); // Ditto
|
I_Assert(pos < 8); // Ditto
|
||||||
|
|
@ -537,29 +573,32 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done
|
if (specialStage.active == false)
|
||||||
|| position == 1) // No SPB for 1st ever
|
|
||||||
{
|
{
|
||||||
return 0;
|
if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done
|
||||||
}
|
|| position == 1) // No SPB for 1st ever
|
||||||
else
|
|
||||||
{
|
|
||||||
const UINT32 dist = max(0, ((signed)roulette->secondToFirst) - SPBSTARTDIST);
|
|
||||||
const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST;
|
|
||||||
const fixed_t maxOdds = 20 << FRACBITS;
|
|
||||||
fixed_t multiplier = FixedDiv(dist, distRange);
|
|
||||||
|
|
||||||
if (multiplier < 0)
|
|
||||||
{
|
{
|
||||||
multiplier = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (multiplier > FRACUNIT)
|
|
||||||
{
|
{
|
||||||
multiplier = FRACUNIT;
|
const UINT32 dist = max(0, ((signed)roulette->secondToFirst) - SPBSTARTDIST);
|
||||||
}
|
const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST;
|
||||||
|
const fixed_t maxOdds = 20 << FRACBITS;
|
||||||
|
fixed_t multiplier = FixedDiv(dist, distRange);
|
||||||
|
|
||||||
newOdds = FixedMul(maxOdds, multiplier);
|
if (multiplier < 0)
|
||||||
|
{
|
||||||
|
multiplier = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multiplier > FRACUNIT)
|
||||||
|
{
|
||||||
|
multiplier = FRACUNIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
newOdds = FixedMul(maxOdds, multiplier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -666,6 +705,11 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett
|
||||||
oddsValid[i] = false;
|
oddsValid[i] = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
else if (specialStage.active == true && i > 3)
|
||||||
|
{
|
||||||
|
oddsValid[i] = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 1; j < NUMKARTRESULTS; j++)
|
for (j = 1; j < NUMKARTRESULTS; j++)
|
||||||
{
|
{
|
||||||
|
|
@ -690,14 +734,24 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SETUPDISTTABLE(0,1);
|
if (specialStage.active == true) // Special Stages
|
||||||
SETUPDISTTABLE(1,1);
|
{
|
||||||
SETUPDISTTABLE(2,1);
|
SETUPDISTTABLE(0,2);
|
||||||
SETUPDISTTABLE(3,2);
|
SETUPDISTTABLE(1,2);
|
||||||
SETUPDISTTABLE(4,2);
|
SETUPDISTTABLE(2,3);
|
||||||
SETUPDISTTABLE(5,3);
|
SETUPDISTTABLE(3,1);
|
||||||
SETUPDISTTABLE(6,3);
|
}
|
||||||
SETUPDISTTABLE(7,1);
|
else
|
||||||
|
{
|
||||||
|
SETUPDISTTABLE(0,1);
|
||||||
|
SETUPDISTTABLE(1,1);
|
||||||
|
SETUPDISTTABLE(2,1);
|
||||||
|
SETUPDISTTABLE(3,2);
|
||||||
|
SETUPDISTTABLE(4,2);
|
||||||
|
SETUPDISTTABLE(5,3);
|
||||||
|
SETUPDISTTABLE(6,3);
|
||||||
|
SETUPDISTTABLE(7,1);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < totalSize; i++)
|
for (i = 0; i < totalSize; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -754,6 +808,11 @@ static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulett
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (specialStage.active == true)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (player == NULL)
|
if (player == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -845,19 +904,36 @@ static void K_InitRoulette(itemroulette_t *const roulette)
|
||||||
roulette->exiting++;
|
roulette->exiting++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (players[i].position == 1)
|
if (specialStage.active == true)
|
||||||
{
|
{
|
||||||
roulette->firstDist = K_UndoMapScaling(players[i].distancetofinish);
|
UINT32 dis = K_UndoMapScaling(players[i].distancetofinish);
|
||||||
|
if (dis < roulette->secondDist)
|
||||||
|
{
|
||||||
|
roulette->secondDist = dis;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (players[i].position == 2)
|
|
||||||
{
|
{
|
||||||
roulette->secondDist = K_UndoMapScaling(players[i].distancetofinish);
|
if (players[i].position == 1)
|
||||||
|
{
|
||||||
|
roulette->firstDist = K_UndoMapScaling(players[i].distancetofinish);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (players[i].position == 2)
|
||||||
|
{
|
||||||
|
roulette->secondDist = K_UndoMapScaling(players[i].distancetofinish);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (specialStage.active == true)
|
||||||
|
{
|
||||||
|
roulette->firstDist = K_UndoMapScaling(K_GetSpecialUFODistance());
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate 2nd's distance from 1st, for SPB
|
// Calculate 2nd's distance from 1st, for SPB
|
||||||
if (roulette->firstDist != UINT32_MAX && roulette->secondDist != UINT32_MAX)
|
if (roulette->firstDist != UINT32_MAX && roulette->secondDist != UINT32_MAX
|
||||||
|
&& roulette->secondDist > roulette->firstDist)
|
||||||
{
|
{
|
||||||
roulette->secondToFirst = roulette->secondDist - roulette->firstDist;
|
roulette->secondToFirst = roulette->secondDist - roulette->firstDist;
|
||||||
roulette->secondToFirst = K_ScaleItemDistance(roulette->secondToFirst, 16 - roulette->playing); // Reversed scaling
|
roulette->secondToFirst = K_ScaleItemDistance(roulette->secondToFirst, 16 - roulette->playing); // Reversed scaling
|
||||||
|
|
@ -964,7 +1040,7 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette)
|
||||||
fixed_t progress = 0;
|
fixed_t progress = 0;
|
||||||
fixed_t total = 0;
|
fixed_t total = 0;
|
||||||
|
|
||||||
if (modeattacking || roulette->playing <= 1)
|
if (K_TimeAttackRules() == true)
|
||||||
{
|
{
|
||||||
// Time Attack rules; use a consistent speed.
|
// Time Attack rules; use a consistent speed.
|
||||||
roulette->tics = roulette->speed = ROULETTE_SPEED_TIMEATTACK;
|
roulette->tics = roulette->speed = ROULETTE_SPEED_TIMEATTACK;
|
||||||
|
|
@ -1047,7 +1123,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (modeattacking || roulette->playing <= 1)
|
else if (K_TimeAttackRules() == true)
|
||||||
{
|
{
|
||||||
switch (gametype)
|
switch (gametype)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#include "st_stuff.h"
|
#include "st_stuff.h"
|
||||||
#include "z_zone.h"
|
#include "z_zone.h"
|
||||||
#include "k_waypoint.h"
|
#include "k_waypoint.h"
|
||||||
|
#include "k_objects.h"
|
||||||
|
|
||||||
struct specialStage specialStage;
|
struct specialStage specialStage;
|
||||||
|
|
||||||
|
|
@ -43,6 +44,7 @@ void K_InitSpecialStage(void)
|
||||||
INT32 i;
|
INT32 i;
|
||||||
|
|
||||||
specialStage.beamDist = UINT32_MAX; // TODO: make proper value
|
specialStage.beamDist = UINT32_MAX; // TODO: make proper value
|
||||||
|
P_SetTarget(&specialStage.ufo, Obj_CreateSpecialUFO());
|
||||||
|
|
||||||
for (i = 0; i < MAXPLAYERS; i++)
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ extern struct specialStage
|
||||||
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars
|
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars
|
||||||
|
|
||||||
UINT32 beamDist; ///< Where the exit beam is.
|
UINT32 beamDist; ///< Where the exit beam is.
|
||||||
mobj_t *capsule; ///< The Chaos Emerald capsule.
|
mobj_t *ufo; ///< The Chaos Emerald capsule.
|
||||||
} specialStage;
|
} specialStage;
|
||||||
|
|
||||||
/*--------------------------------------------------
|
/*--------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1070,6 +1070,45 @@ static boolean K_WaypointPathfindReachedEnd(void *data, void *setupData)
|
||||||
return isEnd;
|
return isEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------
|
||||||
|
static boolean K_WaypointPathfindNextValid(void *data, void *setupData)
|
||||||
|
|
||||||
|
Returns if the current waypoint data has a next waypoint.
|
||||||
|
|
||||||
|
Input Arguments:-
|
||||||
|
data - Should point to a pathfindnode_t to compare
|
||||||
|
setupData - Should point to the pathfindsetup_t to compare
|
||||||
|
|
||||||
|
Return:-
|
||||||
|
True if the waypoint has a next waypoint, false otherwise.
|
||||||
|
--------------------------------------------------*/
|
||||||
|
static boolean K_WaypointPathfindNextValid(void *data, void *setupData)
|
||||||
|
{
|
||||||
|
boolean nextValid = false;
|
||||||
|
|
||||||
|
if (data == NULL || setupData == NULL)
|
||||||
|
{
|
||||||
|
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindNextValid received NULL data.\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pathfindnode_t *node = (pathfindnode_t *)data;
|
||||||
|
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
||||||
|
waypoint_t *wp = (waypoint_t *)node->nodedata;
|
||||||
|
|
||||||
|
if (setup->getconnectednodes == K_WaypointPathfindGetPrev)
|
||||||
|
{
|
||||||
|
nextValid = (wp->numprevwaypoints > 0U);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextValid = (wp->numnextwaypoints > 0U);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextValid;
|
||||||
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------
|
/*--------------------------------------------------
|
||||||
static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData)
|
static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData)
|
||||||
|
|
||||||
|
|
@ -1094,8 +1133,9 @@ static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData)
|
||||||
{
|
{
|
||||||
pathfindnode_t *node = (pathfindnode_t *)data;
|
pathfindnode_t *node = (pathfindnode_t *)data;
|
||||||
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
||||||
|
boolean nextValid = K_WaypointPathfindNextValid(data, setupData);
|
||||||
|
|
||||||
scoreReached = (node->gscore >= setup->endgscore);
|
scoreReached = (node->gscore >= setup->endgscore) || (nextValid == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return scoreReached;
|
return scoreReached;
|
||||||
|
|
@ -1127,8 +1167,9 @@ static boolean K_WaypointPathfindReachedGScoreSpawnable(void *data, void *setupD
|
||||||
pathfindnode_t *node = (pathfindnode_t *)data;
|
pathfindnode_t *node = (pathfindnode_t *)data;
|
||||||
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
||||||
waypoint_t *wp = (waypoint_t *)node->nodedata;
|
waypoint_t *wp = (waypoint_t *)node->nodedata;
|
||||||
|
boolean nextValid = K_WaypointPathfindNextValid(data, setupData);
|
||||||
|
|
||||||
scoreReached = (node->gscore >= setup->endgscore);
|
scoreReached = (node->gscore >= setup->endgscore) || (nextValid == false);
|
||||||
spawnable = K_GetWaypointIsSpawnpoint(wp);
|
spawnable = K_GetWaypointIsSpawnpoint(wp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1251,13 +1292,6 @@ boolean K_PathfindThruCircuit(
|
||||||
"K_PathfindThruCircuit: sourcewaypoint with ID %d has no next waypoint\n",
|
"K_PathfindThruCircuit: sourcewaypoint with ID %d has no next waypoint\n",
|
||||||
K_GetWaypointID(sourcewaypoint));
|
K_GetWaypointID(sourcewaypoint));
|
||||||
}
|
}
|
||||||
else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0))
|
|
||||||
|| ((huntbackwards == true) && (finishline->numnextwaypoints == 0)))
|
|
||||||
{
|
|
||||||
CONS_Debug(DBG_GAMELOGIC,
|
|
||||||
"K_PathfindThruCircuit: finishline with ID %d has no previous waypoint\n",
|
|
||||||
K_GetWaypointID(finishline));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pathfindsetup_t pathfindsetup = {0};
|
pathfindsetup_t pathfindsetup = {0};
|
||||||
|
|
@ -1334,13 +1368,6 @@ boolean K_PathfindThruCircuitSpawnable(
|
||||||
"K_PathfindThruCircuitSpawnable: sourcewaypoint with ID %d has no next waypoint\n",
|
"K_PathfindThruCircuitSpawnable: sourcewaypoint with ID %d has no next waypoint\n",
|
||||||
K_GetWaypointID(sourcewaypoint));
|
K_GetWaypointID(sourcewaypoint));
|
||||||
}
|
}
|
||||||
else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0))
|
|
||||||
|| ((huntbackwards == true) && (finishline->numnextwaypoints == 0)))
|
|
||||||
{
|
|
||||||
CONS_Debug(DBG_GAMELOGIC,
|
|
||||||
"K_PathfindThruCircuitSpawnable: finishline with ID %d has no previous waypoint\n",
|
|
||||||
K_GetWaypointID(finishline));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pathfindsetup_t pathfindsetup = {0};
|
pathfindsetup_t pathfindsetup = {0};
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,5 @@ target_sources(SRB2SDL2 PRIVATE
|
||||||
jawz.c
|
jawz.c
|
||||||
duel-bomb.c
|
duel-bomb.c
|
||||||
broly.c
|
broly.c
|
||||||
|
ufo.c
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#include "../k_waypoint.h"
|
#include "../k_waypoint.h"
|
||||||
#include "../k_respawn.h"
|
#include "../k_respawn.h"
|
||||||
#include "../k_collide.h"
|
#include "../k_collide.h"
|
||||||
|
#include "../k_specialstage.h"
|
||||||
|
|
||||||
#define MAX_JAWZ_TURN (ANGLE_90 / 15) // We can turn a maximum of 6 degrees per frame at regular max speed
|
#define MAX_JAWZ_TURN (ANGLE_90 / 15) // We can turn a maximum of 6 degrees per frame at regular max speed
|
||||||
|
|
||||||
|
|
@ -140,17 +141,21 @@ static void JawzChase(mobj_t *th, boolean grounded)
|
||||||
|
|
||||||
if (jawz_chase(th) == NULL || P_MobjWasRemoved(jawz_chase(th)) == true)
|
if (jawz_chase(th) == NULL || P_MobjWasRemoved(jawz_chase(th)) == true)
|
||||||
{
|
{
|
||||||
|
mobj_t *newChase = NULL;
|
||||||
|
player_t *owner = NULL;
|
||||||
|
|
||||||
th->angle = K_MomentumAngle(th);
|
th->angle = K_MomentumAngle(th);
|
||||||
|
|
||||||
if (jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false
|
if ((jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false)
|
||||||
&& jawz_owner(th)->player != NULL)
|
&& (jawz_owner(th)->player != NULL))
|
||||||
{
|
{
|
||||||
player_t *newPlayer = K_FindJawzTarget(th, jawz_owner(th)->player, ANGLE_90);
|
owner = jawz_owner(th)->player;
|
||||||
|
}
|
||||||
|
|
||||||
if (newPlayer != NULL)
|
newChase = K_FindJawzTarget(th, owner, ANGLE_90);
|
||||||
{
|
if (newChase != NULL)
|
||||||
P_SetTarget(&jawz_chase(th), newPlayer->mo);
|
{
|
||||||
}
|
P_SetTarget(&jawz_chase(th), newChase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,6 +186,11 @@ static void JawzChase(mobj_t *th, boolean grounded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean JawzSteersBetter(void)
|
||||||
|
{
|
||||||
|
return !!!(gametyperules & GTR_CIRCUIT);
|
||||||
|
}
|
||||||
|
|
||||||
void Obj_JawzThink(mobj_t *th)
|
void Obj_JawzThink(mobj_t *th)
|
||||||
{
|
{
|
||||||
mobj_t *ghost = P_SpawnGhostMobj(th);
|
mobj_t *ghost = P_SpawnGhostMobj(th);
|
||||||
|
|
@ -211,7 +221,7 @@ void Obj_JawzThink(mobj_t *th)
|
||||||
ghost->colorized = true;
|
ghost->colorized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(gametyperules & GTR_CIRCUIT))
|
if (JawzSteersBetter() == true)
|
||||||
{
|
{
|
||||||
th->friction = max(0, 3 * th->friction / 4);
|
th->friction = max(0, 3 * th->friction / 4);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "../z_zone.h"
|
#include "../z_zone.h"
|
||||||
#include "../k_waypoint.h"
|
#include "../k_waypoint.h"
|
||||||
#include "../k_respawn.h"
|
#include "../k_respawn.h"
|
||||||
|
#include "../k_specialstage.h"
|
||||||
|
|
||||||
#define SPB_SLIPTIDEDELTA (ANG1 * 3)
|
#define SPB_SLIPTIDEDELTA (ANG1 * 3)
|
||||||
#define SPB_STEERDELTA (ANGLE_90 - ANG10)
|
#define SPB_STEERDELTA (ANGLE_90 - ANG10)
|
||||||
|
|
@ -292,7 +293,7 @@ static boolean SPBSeekSoundPlaying(mobj_t *spb)
|
||||||
|| S_SoundPlaying(spb, sfx_spbskc));
|
|| S_SoundPlaying(spb, sfx_spbskc));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
static void SPBSeek(mobj_t *spb, mobj_t *bestMobj)
|
||||||
{
|
{
|
||||||
const fixed_t desiredSpeed = SPB_DEFAULTSPEED;
|
const fixed_t desiredSpeed = SPB_DEFAULTSPEED;
|
||||||
|
|
||||||
|
|
@ -321,16 +322,15 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
||||||
|
|
||||||
spb_lastplayer(spb) = -1; // Just make sure this is reset
|
spb_lastplayer(spb) = -1; // Just make sure this is reset
|
||||||
|
|
||||||
if (bestPlayer == NULL
|
if (bestMobj == NULL
|
||||||
|| bestPlayer->mo == NULL
|
|| P_MobjWasRemoved(bestMobj) == true
|
||||||
|| P_MobjWasRemoved(bestPlayer->mo) == true
|
|| bestMobj->health <= 0
|
||||||
|| bestPlayer->mo->health <= 0
|
|| (bestMobj->player != NULL && bestMobj->player->respawn.state != RESPAWNST_NONE))
|
||||||
|| (bestPlayer->respawn.state != RESPAWNST_NONE))
|
|
||||||
{
|
{
|
||||||
// No one there? Completely STOP.
|
// No one there? Completely STOP.
|
||||||
spb->momx = spb->momy = spb->momz = 0;
|
spb->momx = spb->momy = spb->momz = 0;
|
||||||
|
|
||||||
if (bestPlayer == NULL)
|
if (bestMobj == NULL)
|
||||||
{
|
{
|
||||||
spbplace = -1;
|
spbplace = -1;
|
||||||
}
|
}
|
||||||
|
|
@ -339,8 +339,16 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Found someone, now get close enough to initiate the slaughter...
|
// Found someone, now get close enough to initiate the slaughter...
|
||||||
P_SetTarget(&spb_chase(spb), bestPlayer->mo);
|
P_SetTarget(&spb_chase(spb), bestMobj);
|
||||||
spbplace = bestPlayer->position;
|
|
||||||
|
if (bestMobj->player != NULL)
|
||||||
|
{
|
||||||
|
spbplace = bestMobj->player->position;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spbplace = 1;
|
||||||
|
}
|
||||||
|
|
||||||
dist = SPBDist(spb, spb_chase(spb));
|
dist = SPBDist(spb, spb_chase(spb));
|
||||||
activeDist = FixedMul(SPB_ACTIVEDIST, spb_chase(spb)->scale);
|
activeDist = FixedMul(SPB_ACTIVEDIST, spb_chase(spb)->scale);
|
||||||
|
|
@ -400,7 +408,18 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
||||||
curWaypoint = K_GetWaypointFromIndex( (size_t)spb_curwaypoint(spb) );
|
curWaypoint = K_GetWaypointFromIndex( (size_t)spb_curwaypoint(spb) );
|
||||||
}
|
}
|
||||||
|
|
||||||
destWaypoint = bestPlayer->nextwaypoint;
|
if (bestMobj->player != NULL)
|
||||||
|
{
|
||||||
|
destWaypoint = bestMobj->player->nextwaypoint;
|
||||||
|
}
|
||||||
|
else if (bestMobj->type == MT_SPECIAL_UFO)
|
||||||
|
{
|
||||||
|
destWaypoint = K_GetSpecialUFOWaypoint(bestMobj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
destWaypoint = K_GetBestWaypointForMobj(bestMobj);
|
||||||
|
}
|
||||||
|
|
||||||
if (curWaypoint != NULL)
|
if (curWaypoint != NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -433,7 +452,8 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
||||||
|
|
||||||
if (pathfindsuccess == true)
|
if (pathfindsuccess == true)
|
||||||
{
|
{
|
||||||
if (cv_spbtest.value) {
|
if (cv_spbtest.value)
|
||||||
|
{
|
||||||
if (pathtoplayer.numnodes > 1)
|
if (pathtoplayer.numnodes > 1)
|
||||||
{
|
{
|
||||||
// Go to the next waypoint.
|
// Go to the next waypoint.
|
||||||
|
|
@ -529,45 +549,48 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
||||||
|
|
||||||
SetSPBSpeed(spb, xySpeed, zSpeed);
|
SetSPBSpeed(spb, xySpeed, zSpeed);
|
||||||
|
|
||||||
// see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh!
|
if (specialStage.active == false)
|
||||||
steerDist = 1536 * mapobjectscale;
|
|
||||||
|
|
||||||
for (i = 0; i < MAXPLAYERS; i++)
|
|
||||||
{
|
{
|
||||||
fixed_t ourDist = INT32_MAX;
|
// see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh!
|
||||||
INT32 ourDelta = INT32_MAX;
|
steerDist = 1536 * mapobjectscale;
|
||||||
|
|
||||||
if (playeringame[i] == false || players[i].spectator == true)
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
{
|
{
|
||||||
// Not in-game
|
fixed_t ourDist = INT32_MAX;
|
||||||
continue;
|
INT32 ourDelta = INT32_MAX;
|
||||||
|
|
||||||
|
if (playeringame[i] == false || players[i].spectator == true)
|
||||||
|
{
|
||||||
|
// Not in-game
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (players[i].mo == NULL || P_MobjWasRemoved(players[i].mo) == true)
|
||||||
|
{
|
||||||
|
// Invalid mobj
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ourDelta = AngleDelta(spb->angle, R_PointToAngle2(spb->x, spb->y, players[i].mo->x, players[i].mo->y));
|
||||||
|
if (ourDelta > SPB_STEERDELTA)
|
||||||
|
{
|
||||||
|
// Check if the angle wouldn't make us LOSE speed.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ourDist = R_PointToDist2(spb->x, spb->y, players[i].mo->x, players[i].mo->y);
|
||||||
|
if (ourDist < steerDist)
|
||||||
|
{
|
||||||
|
steerDist = ourDist;
|
||||||
|
steerMobj = players[i].mo; // it doesn't matter if we override this guy now.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (players[i].mo == NULL || P_MobjWasRemoved(players[i].mo) == true)
|
// different player from our main target, try and ram into em~!
|
||||||
|
if (steerMobj != NULL && steerMobj != spb_chase(spb))
|
||||||
{
|
{
|
||||||
// Invalid mobj
|
P_Thrust(spb, R_PointToAngle2(spb->x, spb->y, steerMobj->x, steerMobj->y), spb_speed(spb) / 4);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ourDelta = AngleDelta(spb->angle, R_PointToAngle2(spb->x, spb->y, players[i].mo->x, players[i].mo->y));
|
|
||||||
if (ourDelta > SPB_STEERDELTA)
|
|
||||||
{
|
|
||||||
// Check if the angle wouldn't make us LOSE speed.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ourDist = R_PointToDist2(spb->x, spb->y, players[i].mo->x, players[i].mo->y);
|
|
||||||
if (ourDist < steerDist)
|
|
||||||
{
|
|
||||||
steerDist = ourDist;
|
|
||||||
steerMobj = players[i].mo; // it doesn't matter if we override this guy now.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// different player from our main target, try and ram into em~!
|
|
||||||
if (steerMobj != NULL && steerMobj != spb_chase(spb))
|
|
||||||
{
|
|
||||||
P_Thrust(spb, R_PointToAngle2(spb->x, spb->y, steerMobj->x, steerMobj->y), spb_speed(spb) / 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sliptide != 0)
|
if (sliptide != 0)
|
||||||
|
|
@ -593,7 +616,7 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SPBChase(mobj_t *spb, player_t *bestPlayer)
|
static void SPBChase(mobj_t *spb, mobj_t *bestMobj)
|
||||||
{
|
{
|
||||||
fixed_t baseSpeed = 0;
|
fixed_t baseSpeed = 0;
|
||||||
fixed_t maxSpeed = 0;
|
fixed_t maxSpeed = 0;
|
||||||
|
|
@ -642,8 +665,8 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer)
|
||||||
S_StartSound(spb, spb->info->activesound);
|
S_StartSound(spb, spb->info->activesound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maybe we want SPB to target an object later? IDK lol
|
|
||||||
chasePlayer = chase->player;
|
chasePlayer = chase->player;
|
||||||
|
|
||||||
if (chasePlayer != NULL)
|
if (chasePlayer != NULL)
|
||||||
{
|
{
|
||||||
UINT8 fracmax = 32;
|
UINT8 fracmax = 32;
|
||||||
|
|
@ -679,7 +702,8 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer)
|
||||||
cy = chasePlayer->cmomy;
|
cy = chasePlayer->cmomy;
|
||||||
|
|
||||||
// Switch targets if you're no longer 1st for long enough
|
// Switch targets if you're no longer 1st for long enough
|
||||||
if (bestPlayer != NULL && chasePlayer->position <= bestPlayer->position)
|
if (bestMobj != NULL
|
||||||
|
&& (bestMobj->player == NULL || chasePlayer->position <= bestMobj->player->position))
|
||||||
{
|
{
|
||||||
spb_modetimer(spb) = SPB_HOTPOTATO;
|
spb_modetimer(spb) = SPB_HOTPOTATO;
|
||||||
}
|
}
|
||||||
|
|
@ -697,6 +721,12 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spb_lastplayer(spb) = -1;
|
||||||
|
spbplace = 1;
|
||||||
|
spb_modetimer(spb) = SPB_HOTPOTATO;
|
||||||
|
}
|
||||||
|
|
||||||
dist = P_AproxDistance(P_AproxDistance(spb->x - chase->x, spb->y - chase->y), spb->z - chase->z);
|
dist = P_AproxDistance(P_AproxDistance(spb->x - chase->x, spb->y - chase->y), spb->z - chase->z);
|
||||||
|
|
||||||
|
|
@ -807,7 +837,7 @@ static void SPBWait(mobj_t *spb)
|
||||||
void Obj_SPBThink(mobj_t *spb)
|
void Obj_SPBThink(mobj_t *spb)
|
||||||
{
|
{
|
||||||
mobj_t *ghost = NULL;
|
mobj_t *ghost = NULL;
|
||||||
player_t *bestPlayer = NULL;
|
mobj_t *bestMobj = NULL;
|
||||||
UINT8 bestRank = UINT8_MAX;
|
UINT8 bestRank = UINT8_MAX;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
|
@ -844,6 +874,15 @@ void Obj_SPBThink(mobj_t *spb)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (specialStage.active == true)
|
||||||
|
{
|
||||||
|
if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false)
|
||||||
|
{
|
||||||
|
bestRank = 1;
|
||||||
|
bestMobj = specialStage.ufo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Find the player with the best rank
|
// Find the player with the best rank
|
||||||
for (i = 0; i < MAXPLAYERS; i++)
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -886,7 +925,7 @@ void Obj_SPBThink(mobj_t *spb)
|
||||||
if (player->position < bestRank)
|
if (player->position < bestRank)
|
||||||
{
|
{
|
||||||
bestRank = player->position;
|
bestRank = player->position;
|
||||||
bestPlayer = player;
|
bestMobj = player->mo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -894,11 +933,11 @@ void Obj_SPBThink(mobj_t *spb)
|
||||||
{
|
{
|
||||||
case SPB_MODE_SEEK:
|
case SPB_MODE_SEEK:
|
||||||
default:
|
default:
|
||||||
SPBSeek(spb, bestPlayer);
|
SPBSeek(spb, bestMobj);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SPB_MODE_CHASE:
|
case SPB_MODE_CHASE:
|
||||||
SPBChase(spb, bestPlayer);
|
SPBChase(spb, bestMobj);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SPB_MODE_WAIT:
|
case SPB_MODE_WAIT:
|
||||||
|
|
@ -970,14 +1009,18 @@ void Obj_SPBExplode(mobj_t *spb)
|
||||||
|
|
||||||
void Obj_SPBTouch(mobj_t *spb, mobj_t *toucher)
|
void Obj_SPBTouch(mobj_t *spb, mobj_t *toucher)
|
||||||
{
|
{
|
||||||
player_t *player = toucher->player;
|
player_t *const player = toucher->player;
|
||||||
|
mobj_t *owner = NULL;
|
||||||
|
mobj_t *chase = NULL;
|
||||||
|
|
||||||
if (spb_intangible(spb) > 0)
|
if (spb_intangible(spb) > 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((spb_owner(spb) == toucher || spb_owner(spb) == toucher->target)
|
owner = spb_owner(spb);
|
||||||
|
|
||||||
|
if ((owner == toucher || owner == toucher->target)
|
||||||
&& (spb_nothink(spb) > 0))
|
&& (spb_nothink(spb) > 0))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
@ -988,30 +1031,34 @@ void Obj_SPBTouch(mobj_t *spb, mobj_t *toucher)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player->spectator == true)
|
if (player != NULL)
|
||||||
{
|
{
|
||||||
return;
|
if (player->spectator == true)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player->bubbleblowup > 0)
|
||||||
|
{
|
||||||
|
// Stun the SPB, and remove the shield.
|
||||||
|
K_DropHnextList(player, false);
|
||||||
|
spb_mode(spb) = SPB_MODE_WAIT;
|
||||||
|
spb_modetimer(spb) = 55; // Slightly over the respawn timer length
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player->bubbleblowup > 0)
|
chase = spb_chase(spb);
|
||||||
{
|
if (chase != NULL && P_MobjWasRemoved(chase) == false
|
||||||
// Stun the SPB, and remove the shield.
|
&& toucher == chase)
|
||||||
K_DropHnextList(player, false);
|
|
||||||
spb_mode(spb) = SPB_MODE_WAIT;
|
|
||||||
spb_modetimer(spb) = 55; // Slightly over the respawn timer length
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spb_chase(spb) != NULL && P_MobjWasRemoved(spb_chase(spb)) == false
|
|
||||||
&& toucher == spb_chase(spb))
|
|
||||||
{
|
{
|
||||||
// Cause the explosion.
|
// Cause the explosion.
|
||||||
Obj_SPBExplode(spb);
|
Obj_SPBExplode(spb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else if (toucher->flags & MF_SHOOTABLE)
|
||||||
{
|
{
|
||||||
// Regular spinout, please.
|
// Regular spinout, please.
|
||||||
P_DamageMobj(toucher, spb, spb_owner(spb), 1, DMG_NORMAL);
|
P_DamageMobj(toucher, spb, owner, 1, DMG_NORMAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
832
src/objects/ufo.c
Normal file
832
src/objects/ufo.c
Normal file
|
|
@ -0,0 +1,832 @@
|
||||||
|
// DR. ROBOTNIK'S RING RACERS
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
|
||||||
|
// Copyright (C) 2022 by Kart Krew
|
||||||
|
//
|
||||||
|
// This program is free software distributed under the
|
||||||
|
// terms of the GNU General Public License, version 2.
|
||||||
|
// See the 'LICENSE' file for more details.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
/// \file ufo.c
|
||||||
|
/// \brief Special Stage UFO + Emerald handler
|
||||||
|
|
||||||
|
#include "../doomdef.h"
|
||||||
|
#include "../doomstat.h"
|
||||||
|
#include "../info.h"
|
||||||
|
#include "../k_kart.h"
|
||||||
|
#include "../k_objects.h"
|
||||||
|
#include "../m_random.h"
|
||||||
|
#include "../p_local.h"
|
||||||
|
#include "../r_main.h"
|
||||||
|
#include "../s_sound.h"
|
||||||
|
#include "../g_game.h"
|
||||||
|
#include "../z_zone.h"
|
||||||
|
#include "../k_waypoint.h"
|
||||||
|
#include "../k_specialstage.h"
|
||||||
|
|
||||||
|
#define UFO_BASE_SPEED (42 * FRACUNIT) // UFO's slowest speed.
|
||||||
|
#define UFO_SPEEDUP (FRACUNIT >> 1) // Acceleration
|
||||||
|
#define UFO_SLOWDOWN (FRACUNIT >> 1) // Deceleration
|
||||||
|
#define UFO_SPACING (768 * FRACUNIT) // How far the UFO wants to stay in front
|
||||||
|
#define UFO_DEADZONE (2048 * FRACUNIT) // Deadzone where it won't update it's speed as much.
|
||||||
|
#define UFO_SPEEDFACTOR (FRACUNIT * 3 / 4) // Factor of player's best speed, to make it more fair.
|
||||||
|
#define UFO_DAMAGED_SPEED (UFO_BASE_SPEED >> 1) // Speed to add when UFO takes damage.
|
||||||
|
#define UFO_START_SPEED (UFO_BASE_SPEED << 1) // Speed when the map starts.
|
||||||
|
|
||||||
|
#define UFO_NUMARMS (3)
|
||||||
|
#define UFO_ARMDELTA (ANGLE_MAX / UFO_NUMARMS)
|
||||||
|
|
||||||
|
#define ufo_waypoint(o) ((o)->extravalue1)
|
||||||
|
#define ufo_distancetofinish(o) ((o)->extravalue2)
|
||||||
|
#define ufo_speed(o) ((o)->watertop)
|
||||||
|
#define ufo_collectdelay(o) ((o)->threshold)
|
||||||
|
|
||||||
|
#define ufo_pieces(o) ((o)->hnext)
|
||||||
|
|
||||||
|
#define ufo_piece_type(o) ((o)->extravalue1)
|
||||||
|
|
||||||
|
#define ufo_piece_owner(o) ((o)->target)
|
||||||
|
#define ufo_piece_next(o) ((o)->hnext)
|
||||||
|
#define ufo_piece_prev(o) ((o)->hprev)
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
UFO_PIECE_TYPE_POD,
|
||||||
|
UFO_PIECE_TYPE_ARM,
|
||||||
|
UFO_PIECE_TYPE_STEM,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void UFOMoveTo(mobj_t *ufo, fixed_t destx, fixed_t desty, fixed_t destz)
|
||||||
|
{
|
||||||
|
ufo->momx = destx - ufo->x;
|
||||||
|
ufo->momy = desty - ufo->y;
|
||||||
|
ufo->momz = destz - ufo->z;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fixed_t GenericDistance(
|
||||||
|
fixed_t curx, fixed_t cury, fixed_t curz,
|
||||||
|
fixed_t destx, fixed_t desty, fixed_t destz)
|
||||||
|
{
|
||||||
|
return P_AproxDistance(P_AproxDistance(destx - curx, desty - cury), destz - curz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean UFOEmeraldChase(mobj_t *ufo)
|
||||||
|
{
|
||||||
|
return (ufo->health <= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean UFOPieceValid(mobj_t *piece)
|
||||||
|
{
|
||||||
|
return (piece != NULL && P_MobjWasRemoved(piece) == false && piece->health > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UFOUpdateDistanceToFinish(mobj_t *ufo)
|
||||||
|
{
|
||||||
|
waypoint_t *finishLine = K_GetFinishLineWaypoint();
|
||||||
|
waypoint_t *nextWaypoint = K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo));
|
||||||
|
|
||||||
|
if (nextWaypoint != NULL && finishLine != NULL)
|
||||||
|
{
|
||||||
|
const boolean useshortcuts = false;
|
||||||
|
const boolean huntbackwards = false;
|
||||||
|
boolean pathfindsuccess = false;
|
||||||
|
path_t pathtofinish = {0};
|
||||||
|
|
||||||
|
pathfindsuccess =
|
||||||
|
K_PathfindToWaypoint(nextWaypoint, finishLine, &pathtofinish, useshortcuts, huntbackwards);
|
||||||
|
|
||||||
|
// Update the UFO's distance to the finish line if a path was found.
|
||||||
|
if (pathfindsuccess == true)
|
||||||
|
{
|
||||||
|
// Add euclidean distance to the next waypoint to the distancetofinish
|
||||||
|
UINT32 adddist;
|
||||||
|
fixed_t disttowaypoint =
|
||||||
|
P_AproxDistance(
|
||||||
|
(ufo->x >> FRACBITS) - (nextWaypoint->mobj->x >> FRACBITS),
|
||||||
|
(ufo->y >> FRACBITS) - (nextWaypoint->mobj->y >> FRACBITS));
|
||||||
|
disttowaypoint = P_AproxDistance(disttowaypoint, (ufo->z >> FRACBITS) - (nextWaypoint->mobj->z >> FRACBITS));
|
||||||
|
|
||||||
|
adddist = (UINT32)disttowaypoint;
|
||||||
|
|
||||||
|
ufo_distancetofinish(ufo) = pathtofinish.totaldist + adddist;
|
||||||
|
Z_Free(pathtofinish.array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UFOUpdateSpeed(mobj_t *ufo)
|
||||||
|
{
|
||||||
|
const fixed_t baseSpeed = FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed));
|
||||||
|
const UINT32 spacing = FixedMul(FixedMul(UFO_SPACING, mapobjectscale), K_GetKartGameSpeedScalar(gamespeed)) >> FRACBITS;
|
||||||
|
const UINT32 deadzone = FixedMul(FixedMul(UFO_DEADZONE, mapobjectscale), K_GetKartGameSpeedScalar(gamespeed)) >> FRACBITS;
|
||||||
|
|
||||||
|
// Best values of all of the players.
|
||||||
|
UINT32 bestDist = UINT32_MAX;
|
||||||
|
fixed_t bestSpeed = 0;
|
||||||
|
|
||||||
|
// Desired values for the UFO itself.
|
||||||
|
UINT32 wantedDist = UINT32_MAX;
|
||||||
|
fixed_t wantedSpeed = ufo_speed(ufo);
|
||||||
|
fixed_t speedDelta = 0;
|
||||||
|
|
||||||
|
UINT8 i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
|
{
|
||||||
|
player_t *player = NULL;
|
||||||
|
|
||||||
|
if (playeringame[i] == false)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
player = &players[i];
|
||||||
|
if (player->spectator == true)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player->distancetofinish < bestDist)
|
||||||
|
{
|
||||||
|
bestDist = player->distancetofinish;
|
||||||
|
|
||||||
|
// Doesn't matter if a splitscreen player behind is moving faster behind the one most caught up.
|
||||||
|
bestSpeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy);
|
||||||
|
bestSpeed = min(bestSpeed, K_GetKartSpeed(player, false, false)); // Don't become unfair with Sneakers.
|
||||||
|
bestSpeed = FixedDiv(bestSpeed, mapobjectscale); // Unscale from mapobjectscale to FRACUNIT
|
||||||
|
bestSpeed = FixedMul(bestSpeed, UFO_SPEEDFACTOR); // Make it a bit more lenient
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestDist == UINT32_MAX)
|
||||||
|
{
|
||||||
|
// Invalid, lets go back to base speed.
|
||||||
|
wantedSpeed = baseSpeed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
INT32 distDelta = 0;
|
||||||
|
|
||||||
|
if (bestDist > spacing)
|
||||||
|
{
|
||||||
|
wantedDist = bestDist - spacing;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wantedDist = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
distDelta = ufo_distancetofinish(ufo) - wantedDist;
|
||||||
|
|
||||||
|
if (distDelta > 0)
|
||||||
|
{
|
||||||
|
// Too far behind! Start speeding up!
|
||||||
|
wantedSpeed = max(bestSpeed, baseSpeed << 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (abs(distDelta) <= deadzone)
|
||||||
|
{
|
||||||
|
// We're in a good spot, try to match the player.
|
||||||
|
wantedSpeed = max(bestSpeed >> 1, baseSpeed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Too far ahead! Start slowing down!
|
||||||
|
wantedSpeed = baseSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slowly accelerate or decelerate to
|
||||||
|
// get to our desired speed.
|
||||||
|
speedDelta = wantedSpeed - ufo_speed(ufo);
|
||||||
|
if (speedDelta > 0)
|
||||||
|
{
|
||||||
|
if (abs(speedDelta) <= UFO_SPEEDUP)
|
||||||
|
{
|
||||||
|
ufo_speed(ufo) = wantedSpeed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ufo_speed(ufo) += UFO_SPEEDUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (speedDelta < 0)
|
||||||
|
{
|
||||||
|
if (abs(speedDelta) <= UFO_SLOWDOWN)
|
||||||
|
{
|
||||||
|
ufo_speed(ufo) = wantedSpeed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ufo_speed(ufo) -= UFO_SLOWDOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UFOUpdateAngle(mobj_t *ufo)
|
||||||
|
{
|
||||||
|
angle_t dest = K_MomentumAngle(ufo);
|
||||||
|
INT32 delta = AngleDeltaSigned(ufo->angle, dest);
|
||||||
|
ufo->angle += delta >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo)
|
||||||
|
{
|
||||||
|
if ((ufo == NULL) && (specialStage.active == true))
|
||||||
|
{
|
||||||
|
ufo = specialStage.ufo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ufo != NULL && P_MobjWasRemoved(ufo) == false
|
||||||
|
&& ufo->type == MT_SPECIAL_UFO)
|
||||||
|
{
|
||||||
|
if (ufo_waypoint(ufo) >= 0)
|
||||||
|
{
|
||||||
|
return K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UFOMove(mobj_t *ufo)
|
||||||
|
{
|
||||||
|
waypoint_t *curWaypoint = NULL;
|
||||||
|
waypoint_t *destWaypoint = NULL;
|
||||||
|
|
||||||
|
fixed_t distLeft = INT32_MAX;
|
||||||
|
fixed_t newX = ufo->x;
|
||||||
|
fixed_t newY = ufo->y;
|
||||||
|
fixed_t newZ = ufo->z;
|
||||||
|
const fixed_t floatHeight = 24 * ufo->scale;
|
||||||
|
|
||||||
|
const boolean useshortcuts = false;
|
||||||
|
const boolean huntbackwards = false;
|
||||||
|
boolean pathfindsuccess = false;
|
||||||
|
path_t pathtofinish = {0};
|
||||||
|
size_t pathIndex = 0;
|
||||||
|
|
||||||
|
boolean reachedEnd = false;
|
||||||
|
|
||||||
|
curWaypoint = K_GetSpecialUFOWaypoint(ufo);
|
||||||
|
destWaypoint = K_GetFinishLineWaypoint();
|
||||||
|
|
||||||
|
if (curWaypoint == NULL || destWaypoint == NULL)
|
||||||
|
{
|
||||||
|
// Waypoints aren't valid.
|
||||||
|
// Just stand still.
|
||||||
|
ufo->momx = 0;
|
||||||
|
ufo->momy = 0;
|
||||||
|
ufo->momz = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
distLeft = FixedMul(ufo_speed(ufo), mapobjectscale);
|
||||||
|
|
||||||
|
while (distLeft > 0)
|
||||||
|
{
|
||||||
|
fixed_t wpX = curWaypoint->mobj->x;
|
||||||
|
fixed_t wpY = curWaypoint->mobj->y;
|
||||||
|
fixed_t wpZ = curWaypoint->mobj->z + floatHeight;
|
||||||
|
|
||||||
|
fixed_t distToNext = GenericDistance(
|
||||||
|
newX, newY, newZ,
|
||||||
|
wpX, wpY, wpZ
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distToNext > distLeft)
|
||||||
|
{
|
||||||
|
// Only made it partially there.
|
||||||
|
newX += FixedMul(FixedDiv(wpX - newX, distToNext), distLeft);
|
||||||
|
newY += FixedMul(FixedDiv(wpY - newY, distToNext), distLeft);
|
||||||
|
newZ += FixedMul(FixedDiv(wpZ - newZ, distToNext), distLeft);
|
||||||
|
|
||||||
|
distLeft = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Close enough to the next waypoint,
|
||||||
|
// move there and remove the distance.
|
||||||
|
newX = wpX;
|
||||||
|
newY = wpY;
|
||||||
|
newZ = wpZ;
|
||||||
|
|
||||||
|
distLeft -= distToNext;
|
||||||
|
|
||||||
|
if (curWaypoint == destWaypoint)
|
||||||
|
{
|
||||||
|
// Reached the end.
|
||||||
|
reachedEnd = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create waypoint path to our destination.
|
||||||
|
// Crazy over-engineered, just to catch when
|
||||||
|
// waypoints are insanely close to each other :P
|
||||||
|
if (pathfindsuccess == false)
|
||||||
|
{
|
||||||
|
pathfindsuccess = K_PathfindToWaypoint(
|
||||||
|
curWaypoint, destWaypoint,
|
||||||
|
&pathtofinish,
|
||||||
|
useshortcuts, huntbackwards
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pathfindsuccess == false)
|
||||||
|
{
|
||||||
|
// Path isn't valid.
|
||||||
|
// Just keep going.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pathIndex++;
|
||||||
|
|
||||||
|
if (pathIndex >= pathtofinish.numnodes)
|
||||||
|
{
|
||||||
|
// Successfully reached the end of the path.
|
||||||
|
reachedEnd = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now moving to the next waypoint.
|
||||||
|
curWaypoint = (waypoint_t *)pathtofinish.array[pathIndex].nodedata;
|
||||||
|
ufo_waypoint(ufo) = (INT32)K_GetWaypointHeapIndex(curWaypoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UFOMoveTo(ufo, newX, newY, newZ);
|
||||||
|
|
||||||
|
if (reachedEnd == true)
|
||||||
|
{
|
||||||
|
CONS_Printf("You lost...\n");
|
||||||
|
ufo_waypoint(ufo) = -1; // Invalidate
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathfindsuccess == true)
|
||||||
|
{
|
||||||
|
Z_Free(pathtofinish.array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UFOEmeraldVFX(mobj_t *ufo)
|
||||||
|
{
|
||||||
|
const INT32 bobS = 32;
|
||||||
|
const angle_t bobA = (leveltime & (bobS - 1)) * (ANGLE_MAX / bobS);
|
||||||
|
const fixed_t bobH = 16 * ufo->scale;
|
||||||
|
|
||||||
|
ufo->sprzoff = FixedMul(bobH, FINESINE(bobA >> ANGLETOFINESHIFT));
|
||||||
|
|
||||||
|
if (leveltime % 3 == 0)
|
||||||
|
{
|
||||||
|
mobj_t *sparkle = P_SpawnMobjFromMobj(
|
||||||
|
ufo,
|
||||||
|
P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT,
|
||||||
|
P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT,
|
||||||
|
(P_RandomRange(PR_SPARKLE, 0, 64) * FRACUNIT) + FixedDiv(ufo->sprzoff, ufo->scale),
|
||||||
|
MT_EMERALDSPARK
|
||||||
|
);
|
||||||
|
|
||||||
|
sparkle->color = ufo->color;
|
||||||
|
sparkle->momz += 8 * ufo->scale * P_MobjFlip(ufo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Obj_SpecialUFOThinker(mobj_t *ufo)
|
||||||
|
{
|
||||||
|
UFOMove(ufo);
|
||||||
|
UFOUpdateAngle(ufo);
|
||||||
|
UFOUpdateDistanceToFinish(ufo);
|
||||||
|
UFOUpdateSpeed(ufo);
|
||||||
|
|
||||||
|
if (UFOEmeraldChase(ufo) == true)
|
||||||
|
{
|
||||||
|
// Spawn emerald sparkles
|
||||||
|
UFOEmeraldVFX(ufo);
|
||||||
|
ufo_collectdelay(ufo)--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ufo_collectdelay(ufo) = TICRATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UFOCopyHitlagToPieces(mobj_t *ufo)
|
||||||
|
{
|
||||||
|
mobj_t *piece = NULL;
|
||||||
|
|
||||||
|
piece = ufo_pieces(ufo);
|
||||||
|
while (UFOPieceValid(piece) == true)
|
||||||
|
{
|
||||||
|
piece->hitlag = ufo->hitlag;
|
||||||
|
piece->eflags = (piece->eflags & ~MFE_DAMAGEHITLAG) | (ufo->eflags & MFE_DAMAGEHITLAG);
|
||||||
|
piece = ufo_piece_next(piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UFOKillPiece(mobj_t *piece)
|
||||||
|
{
|
||||||
|
angle_t dir = ANGLE_MAX;
|
||||||
|
fixed_t thrust = 0;
|
||||||
|
|
||||||
|
if (UFOPieceValid(piece) == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
piece->health = 0;
|
||||||
|
piece->tics = TICRATE;
|
||||||
|
piece->flags &= ~MF_NOGRAVITY;
|
||||||
|
|
||||||
|
switch (ufo_piece_type(piece))
|
||||||
|
{
|
||||||
|
case UFO_PIECE_TYPE_STEM:
|
||||||
|
{
|
||||||
|
piece->tics = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case UFO_PIECE_TYPE_ARM:
|
||||||
|
{
|
||||||
|
dir = piece->angle;
|
||||||
|
thrust = 12 * piece->scale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
dir = FixedAngle(P_RandomRange(PR_DECORATION, 0, 359) << FRACBITS);
|
||||||
|
thrust = 4 * piece->scale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
P_Thrust(piece, dir, -thrust);
|
||||||
|
P_SetObjectMomZ(piece, 12*FRACUNIT, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UFOKillPieces(mobj_t *ufo)
|
||||||
|
{
|
||||||
|
mobj_t *piece = NULL;
|
||||||
|
|
||||||
|
piece = ufo_pieces(ufo);
|
||||||
|
while (UFOPieceValid(piece) == true)
|
||||||
|
{
|
||||||
|
UFOKillPiece(piece);
|
||||||
|
piece = ufo_piece_next(piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT8 GetUFODamage(mobj_t *inflictor, UINT8 damageType)
|
||||||
|
{
|
||||||
|
if (inflictor != NULL && P_MobjWasRemoved(inflictor) == false)
|
||||||
|
{
|
||||||
|
switch (inflictor->type)
|
||||||
|
{
|
||||||
|
case MT_JAWZ_SHIELD:
|
||||||
|
case MT_ORBINAUT_SHIELD:
|
||||||
|
{
|
||||||
|
// Shields deal chip damage.
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
case MT_JAWZ:
|
||||||
|
{
|
||||||
|
// Thrown Jawz deal a bit extra.
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
case MT_ORBINAUT:
|
||||||
|
{
|
||||||
|
// Thrown orbinauts deal double damage.
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
case MT_SPB:
|
||||||
|
{
|
||||||
|
// SPB deals triple damage.
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
case MT_BANANA:
|
||||||
|
{
|
||||||
|
// Banana snipes deal triple damage,
|
||||||
|
// laid down bananas deal regular damage.
|
||||||
|
if (inflictor->health > 1)
|
||||||
|
{
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
case MT_PLAYER:
|
||||||
|
{
|
||||||
|
// Players deal damage relative to how many sneakers they used.
|
||||||
|
return 15 * max(1, inflictor->player->numsneakers);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guess from damage type.
|
||||||
|
switch (damageType & DMG_TYPEMASK)
|
||||||
|
{
|
||||||
|
case DMG_NORMAL:
|
||||||
|
case DMG_STING:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
case DMG_WIPEOUT:
|
||||||
|
{
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
case DMG_EXPLODE:
|
||||||
|
case DMG_TUMBLE:
|
||||||
|
{
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
case DMG_VOLTAGE:
|
||||||
|
{
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType)
|
||||||
|
{
|
||||||
|
const fixed_t addSpeed = FixedMul(UFO_DAMAGED_SPEED, K_GetKartGameSpeedScalar(gamespeed));
|
||||||
|
UINT8 damage = 1;
|
||||||
|
|
||||||
|
(void)source;
|
||||||
|
|
||||||
|
if (UFOEmeraldChase(ufo) == true)
|
||||||
|
{
|
||||||
|
// Damaged fully already, no need for any more.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
damage = GetUFODamage(inflictor, damageType);
|
||||||
|
|
||||||
|
if (damage <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speed up on damage!
|
||||||
|
ufo_speed(ufo) += addSpeed;
|
||||||
|
|
||||||
|
K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true);
|
||||||
|
UFOCopyHitlagToPieces(ufo);
|
||||||
|
|
||||||
|
if (damage >= ufo->health - 1)
|
||||||
|
{
|
||||||
|
// Destroy the UFO parts, and make the emerald collectible!
|
||||||
|
UFOKillPieces(ufo);
|
||||||
|
|
||||||
|
ufo->health = 1;
|
||||||
|
ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW);
|
||||||
|
ufo->shadowscale = FRACUNIT/3;
|
||||||
|
|
||||||
|
ufo_speed(ufo) += addSpeed; // Even more speed!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ufo->health -= damage;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other)
|
||||||
|
{
|
||||||
|
if (other->player == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((other->player->sneakertimer > 0)
|
||||||
|
&& !P_PlayerInPain(other->player)
|
||||||
|
&& (other->player->flashing == 0))
|
||||||
|
{
|
||||||
|
// Bump and deal damage.
|
||||||
|
Obj_SpecialUFODamage(ufo, other, other, DMG_STEAL);
|
||||||
|
K_KartBouncing(other, ufo);
|
||||||
|
other->player->sneakertimer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Obj_UFOPieceThink(mobj_t *piece)
|
||||||
|
{
|
||||||
|
mobj_t *ufo = ufo_piece_owner(piece);
|
||||||
|
|
||||||
|
if (ufo == NULL || P_MobjWasRemoved(ufo) == true)
|
||||||
|
{
|
||||||
|
P_KillMobj(piece, NULL, NULL, DMG_NORMAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
piece->destscale = 3 * ufo->destscale / 2;
|
||||||
|
piece->scalespeed = ufo->scalespeed;
|
||||||
|
|
||||||
|
switch (ufo_piece_type(piece))
|
||||||
|
{
|
||||||
|
case UFO_PIECE_TYPE_POD:
|
||||||
|
{
|
||||||
|
UFOMoveTo(piece, ufo->x, ufo->y, ufo->z + (132 * piece->scale));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UFO_PIECE_TYPE_ARM:
|
||||||
|
{
|
||||||
|
fixed_t dis = (88 * piece->scale);
|
||||||
|
|
||||||
|
fixed_t x = ufo->x - FixedMul(dis, FINECOSINE(piece->angle >> ANGLETOFINESHIFT));
|
||||||
|
fixed_t y = ufo->y - FixedMul(dis, FINESINE(piece->angle >> ANGLETOFINESHIFT));
|
||||||
|
|
||||||
|
UFOMoveTo(piece, x, y, ufo->z + (24 * piece->scale));
|
||||||
|
|
||||||
|
piece->angle -= FixedMul(ANG2, FixedDiv(ufo_speed(ufo), UFO_BASE_SPEED));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UFO_PIECE_TYPE_STEM:
|
||||||
|
{
|
||||||
|
fixed_t stemZ = ufo->z + (294 * piece->scale);
|
||||||
|
fixed_t sc = FixedDiv(FixedDiv(ufo->ceilingz - stemZ, piece->scale), 15 * FRACUNIT);
|
||||||
|
|
||||||
|
UFOMoveTo(piece, ufo->x, ufo->y, stemZ);
|
||||||
|
piece->spriteyscale = sc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
P_RemoveMobj(piece);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Obj_UFOPieceDead(mobj_t *piece)
|
||||||
|
{
|
||||||
|
piece->renderflags ^= RF_DONTDRAW;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Obj_UFOPieceRemoved(mobj_t *piece)
|
||||||
|
{
|
||||||
|
// Repair piece list.
|
||||||
|
mobj_t *ufo = ufo_piece_owner(piece);
|
||||||
|
mobj_t *next = ufo_piece_next(piece);
|
||||||
|
mobj_t *prev = ufo_piece_prev(piece);
|
||||||
|
|
||||||
|
if (prev != NULL && P_MobjWasRemoved(prev) == false)
|
||||||
|
{
|
||||||
|
P_SetTarget(
|
||||||
|
&ufo_piece_next(prev),
|
||||||
|
(next != NULL && P_MobjWasRemoved(next) == false) ? next : NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next != NULL && P_MobjWasRemoved(next) == false)
|
||||||
|
{
|
||||||
|
P_SetTarget(
|
||||||
|
&ufo_piece_prev(next),
|
||||||
|
(prev != NULL && P_MobjWasRemoved(prev) == false) ? prev : NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ufo != NULL && P_MobjWasRemoved(ufo) == false)
|
||||||
|
{
|
||||||
|
if (piece == ufo_pieces(ufo))
|
||||||
|
{
|
||||||
|
P_SetTarget(
|
||||||
|
&ufo_pieces(ufo),
|
||||||
|
(next != NULL && P_MobjWasRemoved(next) == false) ? next : NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
P_SetTarget(&ufo_piece_next(piece), NULL);
|
||||||
|
P_SetTarget(&ufo_piece_prev(piece), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static mobj_t *InitSpecialUFO(waypoint_t *start)
|
||||||
|
{
|
||||||
|
mobj_t *ufo = NULL;
|
||||||
|
mobj_t *overlay = NULL;
|
||||||
|
mobj_t *piece = NULL;
|
||||||
|
mobj_t *prevPiece = NULL;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (start == NULL)
|
||||||
|
{
|
||||||
|
// Simply create at the origin with default values.
|
||||||
|
ufo = P_SpawnMobj(0, 0, 0, MT_SPECIAL_UFO);
|
||||||
|
ufo_waypoint(ufo) = -1; // Invalidate
|
||||||
|
ufo_distancetofinish(ufo) = INT32_MAX;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Create with a proper waypoint track!
|
||||||
|
ufo = P_SpawnMobj(start->mobj->x, start->mobj->y, start->mobj->z, MT_SPECIAL_UFO);
|
||||||
|
ufo_waypoint(ufo) = (INT32)K_GetWaypointHeapIndex(start);
|
||||||
|
UFOUpdateDistanceToFinish(ufo);
|
||||||
|
}
|
||||||
|
|
||||||
|
ufo_speed(ufo) = FixedMul(UFO_START_SPEED, K_GetKartGameSpeedScalar(gamespeed));
|
||||||
|
|
||||||
|
// TODO: Adjustable Special Stage emerald color
|
||||||
|
ufo->color = SKINCOLOR_CHAOSEMERALD1;
|
||||||
|
|
||||||
|
overlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY);
|
||||||
|
P_SetTarget(&overlay->target, ufo);
|
||||||
|
overlay->color = ufo->color;
|
||||||
|
|
||||||
|
// TODO: Super Emeralds / Chaos Rings
|
||||||
|
P_SetMobjState(overlay, S_CHAOSEMERALD_UNDER);
|
||||||
|
|
||||||
|
// Create UFO pieces.
|
||||||
|
// First: UFO center.
|
||||||
|
piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE);
|
||||||
|
P_SetTarget(&ufo_piece_owner(piece), ufo);
|
||||||
|
|
||||||
|
P_SetMobjState(piece, S_SPECIAL_UFO_POD);
|
||||||
|
ufo_piece_type(piece) = UFO_PIECE_TYPE_POD;
|
||||||
|
|
||||||
|
overlay = P_SpawnMobjFromMobj(piece, 0, 0, 0, MT_OVERLAY);
|
||||||
|
P_SetTarget(&overlay->target, piece);
|
||||||
|
P_SetMobjState(overlay, S_SPECIAL_UFO_OVERLAY);
|
||||||
|
|
||||||
|
P_SetTarget(&ufo_pieces(ufo), piece);
|
||||||
|
prevPiece = piece;
|
||||||
|
|
||||||
|
// Add the catcher arms.
|
||||||
|
for (i = 0; i < UFO_NUMARMS; i++)
|
||||||
|
{
|
||||||
|
piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE);
|
||||||
|
P_SetTarget(&ufo_piece_owner(piece), ufo);
|
||||||
|
|
||||||
|
P_SetMobjState(piece, S_SPECIAL_UFO_ARM);
|
||||||
|
ufo_piece_type(piece) = UFO_PIECE_TYPE_ARM;
|
||||||
|
|
||||||
|
piece->angle = UFO_ARMDELTA * i;
|
||||||
|
|
||||||
|
P_SetTarget(&ufo_piece_next(prevPiece), piece);
|
||||||
|
P_SetTarget(&ufo_piece_prev(piece), prevPiece);
|
||||||
|
prevPiece = piece;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the stem.
|
||||||
|
piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE);
|
||||||
|
P_SetTarget(&ufo_piece_owner(piece), ufo);
|
||||||
|
|
||||||
|
P_SetMobjState(piece, S_SPECIAL_UFO_STEM);
|
||||||
|
ufo_piece_type(piece) = UFO_PIECE_TYPE_STEM;
|
||||||
|
|
||||||
|
P_SetTarget(&ufo_piece_next(prevPiece), piece);
|
||||||
|
P_SetTarget(&ufo_piece_prev(piece), prevPiece);
|
||||||
|
prevPiece = piece;
|
||||||
|
|
||||||
|
return ufo;
|
||||||
|
}
|
||||||
|
|
||||||
|
mobj_t *Obj_CreateSpecialUFO(void)
|
||||||
|
{
|
||||||
|
waypoint_t *finishWaypoint = K_GetFinishLineWaypoint();
|
||||||
|
waypoint_t *startWaypoint = NULL;
|
||||||
|
|
||||||
|
if (finishWaypoint != NULL)
|
||||||
|
{
|
||||||
|
const boolean huntbackwards = true;
|
||||||
|
const boolean useshortcuts = false;
|
||||||
|
const UINT32 traveldist = INT32_MAX; // Go as far back as possible. Not UINT32_MAX to avoid possible overflow.
|
||||||
|
boolean pathfindsuccess = false;
|
||||||
|
path_t pathtofinish = {0};
|
||||||
|
|
||||||
|
pathfindsuccess = K_PathfindThruCircuit(
|
||||||
|
finishWaypoint, traveldist,
|
||||||
|
&pathtofinish,
|
||||||
|
useshortcuts, huntbackwards
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pathfindsuccess == true)
|
||||||
|
{
|
||||||
|
startWaypoint = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata;
|
||||||
|
Z_Free(pathtofinish.array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return InitSpecialUFO(startWaypoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT32 K_GetSpecialUFODistance(void)
|
||||||
|
{
|
||||||
|
if (specialStage.active == true)
|
||||||
|
{
|
||||||
|
if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false)
|
||||||
|
{
|
||||||
|
return (UINT32)ufo_distancetofinish(specialStage.ufo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
@ -386,11 +386,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
||||||
special->target->player->karmadelay = comebacktime;
|
special->target->player->karmadelay = comebacktime;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case MT_SPB:
|
|
||||||
{
|
|
||||||
Obj_SPBTouch(special, toucher);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case MT_DUELBOMB:
|
case MT_DUELBOMB:
|
||||||
{
|
{
|
||||||
Obj_DuelBombTouch(special, toucher);
|
Obj_DuelBombTouch(special, toucher);
|
||||||
|
|
@ -409,6 +404,18 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
||||||
player->emeralds |= special->extravalue1;
|
player->emeralds |= special->extravalue1;
|
||||||
K_CheckEmeralds(player);
|
K_CheckEmeralds(player);
|
||||||
break;
|
break;
|
||||||
|
case MT_SPECIAL_UFO:
|
||||||
|
if (!P_CanPickupItem(player, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (special->threshold > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (toucher->hitlag > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CONS_Printf("You win!\n");
|
||||||
|
break;
|
||||||
/*
|
/*
|
||||||
case MT_EERIEFOG:
|
case MT_EERIEFOG:
|
||||||
special->frame &= ~FF_TRANS80;
|
special->frame &= ~FF_TRANS80;
|
||||||
|
|
@ -1402,7 +1409,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
|
||||||
INT16 spacing = (target->radius >> 1) / target->scale;
|
INT16 spacing = (target->radius >> 1) / target->scale;
|
||||||
|
|
||||||
// set respawn fuse
|
// set respawn fuse
|
||||||
if (modeattacking) // no respawns
|
if (K_TimeAttackRules() == true) // no respawns
|
||||||
;
|
;
|
||||||
else if (target->threshold == KITEM_SUPERRING)
|
else if (target->threshold == KITEM_SUPERRING)
|
||||||
target->fuse = 20*TICRATE;
|
target->fuse = 20*TICRATE;
|
||||||
|
|
@ -2289,7 +2296,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
||||||
|
|
||||||
K_PlayPainSound(target, source);
|
K_PlayPainSound(target, source);
|
||||||
|
|
||||||
if ((hardhit == true) || (cv_kartdebughuddrop.value && !modeattacking))
|
if ((hardhit == true) || cv_kartdebughuddrop.value)
|
||||||
{
|
{
|
||||||
K_DropItems(player);
|
K_DropItems(player);
|
||||||
}
|
}
|
||||||
|
|
@ -2311,6 +2318,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (target->type == MT_SPECIAL_UFO)
|
||||||
|
{
|
||||||
|
return Obj_SpecialUFODamage(target, inflictor, source, damagetype);
|
||||||
|
}
|
||||||
|
|
||||||
if (damagetype & DMG_STEAL)
|
if (damagetype & DMG_STEAL)
|
||||||
{
|
{
|
||||||
// Not a player, steal damage is intended to not do anything
|
// Not a player, steal damage is intended to not do anything
|
||||||
|
|
|
||||||
55
src/p_map.c
55
src/p_map.c
|
|
@ -757,6 +757,53 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
||||||
|
|
||||||
// SRB2kart 011617 - Colission[sic] code for kart items //{
|
// SRB2kart 011617 - Colission[sic] code for kart items //{
|
||||||
|
|
||||||
|
if (thing->type == MT_SPB)
|
||||||
|
{
|
||||||
|
if (tm.thing->type != MT_PLAYER
|
||||||
|
&& thing->tracer != tm.thing)
|
||||||
|
{
|
||||||
|
// Needs to be a player or the
|
||||||
|
// thing that we're chasing.
|
||||||
|
return BMIT_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tm.thing->z > thing->z + thing->height)
|
||||||
|
{
|
||||||
|
return BMIT_CONTINUE; // overhead
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tm.thing->z + tm.thing->height < thing->z)
|
||||||
|
{
|
||||||
|
return BMIT_CONTINUE; // underneath
|
||||||
|
}
|
||||||
|
|
||||||
|
Obj_SPBTouch(thing, tm.thing);
|
||||||
|
return BMIT_CONTINUE;
|
||||||
|
}
|
||||||
|
else if (tm.thing->type == MT_SPB)
|
||||||
|
{
|
||||||
|
if (thing->type != MT_PLAYER
|
||||||
|
&& tm.thing->tracer != thing)
|
||||||
|
{
|
||||||
|
// Needs to be a player or the
|
||||||
|
// thing that we're chasing.
|
||||||
|
return BMIT_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tm.thing->z > thing->z + thing->height)
|
||||||
|
{
|
||||||
|
return BMIT_CONTINUE; // overhead
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tm.thing->z + tm.thing->height < thing->z)
|
||||||
|
{
|
||||||
|
return BMIT_CONTINUE; // underneath
|
||||||
|
}
|
||||||
|
|
||||||
|
Obj_SPBTouch(tm.thing, thing);
|
||||||
|
return BMIT_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
if (thing->type == MT_SHRINK_GUN || thing->type == MT_SHRINK_PARTICLE)
|
if (thing->type == MT_SHRINK_GUN || thing->type == MT_SHRINK_PARTICLE)
|
||||||
{
|
{
|
||||||
if (tm.thing->type != MT_PLAYER)
|
if (tm.thing->type != MT_PLAYER)
|
||||||
|
|
@ -1379,6 +1426,14 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
||||||
|
|
||||||
return BMIT_CONTINUE;
|
return BMIT_CONTINUE;
|
||||||
}
|
}
|
||||||
|
else if (thing->type == MT_SPECIAL_UFO)
|
||||||
|
{
|
||||||
|
if (!(thing->flags & MF_SPECIAL))
|
||||||
|
{
|
||||||
|
Obj_PlayerUFOCollide(thing, tm.thing);
|
||||||
|
return BMIT_CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (thing->type == MT_BLUEROBRA_HEAD || thing->type == MT_BLUEROBRA_JOINT)
|
else if (thing->type == MT_BLUEROBRA_HEAD || thing->type == MT_BLUEROBRA_JOINT)
|
||||||
{
|
{
|
||||||
// see if it went over / under
|
// see if it went over / under
|
||||||
|
|
|
||||||
48
src/p_mobj.c
48
src/p_mobj.c
|
|
@ -46,6 +46,7 @@
|
||||||
#include "k_terrain.h"
|
#include "k_terrain.h"
|
||||||
#include "k_collide.h"
|
#include "k_collide.h"
|
||||||
#include "k_objects.h"
|
#include "k_objects.h"
|
||||||
|
#include "k_grandprix.h"
|
||||||
|
|
||||||
static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
|
static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
|
||||||
consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
|
consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
|
||||||
|
|
@ -5306,10 +5307,24 @@ void P_RunOverlays(void)
|
||||||
mo->pitch = mo->target->pitch;
|
mo->pitch = mo->target->pitch;
|
||||||
mo->roll = mo->target->roll;
|
mo->roll = mo->target->roll;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
mo->spritexoffset = mo->target->spritexoffset;
|
||||||
|
mo->spriteyoffset = mo->target->spriteyoffset;
|
||||||
|
mo->spritexscale = mo->target->spritexscale;
|
||||||
|
mo->spriteyscale = mo->target->spriteyscale;
|
||||||
|
|
||||||
|
mo->sprxoff = mo->target->sprxoff;
|
||||||
|
mo->spryoff = mo->target->spryoff;
|
||||||
|
mo->sprzoff = mo->target->sprzoff;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mo->hitlag = mo->target->hitlag;
|
||||||
|
mo->eflags = (mo->eflags & ~MFE_DAMAGEHITLAG) | (mo->target->eflags & MFE_DAMAGEHITLAG);
|
||||||
|
|
||||||
if ((mo->flags & MF_DONTENCOREMAP) != (mo->target->flags & MF_DONTENCOREMAP))
|
if ((mo->flags & MF_DONTENCOREMAP) != (mo->target->flags & MF_DONTENCOREMAP))
|
||||||
mo->flags ^= MF_DONTENCOREMAP;
|
mo->flags ^= MF_DONTENCOREMAP;
|
||||||
|
|
||||||
mo->dispoffset = mo->target->dispoffset + mo->info->dispoffset;
|
mo->dispoffset = mo->target->dispoffset;
|
||||||
|
|
||||||
if (!(mo->state->frame & FF_ANIMATE))
|
if (!(mo->state->frame & FF_ANIMATE))
|
||||||
{
|
{
|
||||||
|
|
@ -5329,6 +5344,7 @@ void P_RunOverlays(void)
|
||||||
// if you're using FF_ANIMATE on an overlay,
|
// if you're using FF_ANIMATE on an overlay,
|
||||||
// then you're on your own.
|
// then you're on your own.
|
||||||
zoffs = 0;
|
zoffs = 0;
|
||||||
|
mo->dispoffset++;
|
||||||
}
|
}
|
||||||
|
|
||||||
P_UnsetThingPosition(mo);
|
P_UnsetThingPosition(mo);
|
||||||
|
|
@ -6755,6 +6771,11 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
|
||||||
S_StartSound(dust, sfx_s3k3d);
|
S_StartSound(dust, sfx_s3k3d);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MT_SPECIAL_UFO_PIECE:
|
||||||
|
{
|
||||||
|
Obj_UFOPieceDead(mobj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -7366,6 +7387,16 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
||||||
Obj_DuelBombThink(mobj);
|
Obj_DuelBombThink(mobj);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case MT_SPECIAL_UFO:
|
||||||
|
{
|
||||||
|
Obj_SpecialUFOThinker(mobj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MT_SPECIAL_UFO_PIECE:
|
||||||
|
{
|
||||||
|
Obj_UFOPieceThink(mobj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case MT_EMERALD:
|
case MT_EMERALD:
|
||||||
{
|
{
|
||||||
if (battleovertime.enabled >= 10*TICRATE)
|
if (battleovertime.enabled >= 10*TICRATE)
|
||||||
|
|
@ -10121,6 +10152,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing)
|
||||||
case MT_PLAYER:
|
case MT_PLAYER:
|
||||||
case MT_KART_LEFTOVER:
|
case MT_KART_LEFTOVER:
|
||||||
case MT_BATTLECAPSULE:
|
case MT_BATTLECAPSULE:
|
||||||
|
case MT_SPECIAL_UFO:
|
||||||
thing->shadowscale = FRACUNIT;
|
thing->shadowscale = FRACUNIT;
|
||||||
break;
|
break;
|
||||||
case MT_SMALLMACE:
|
case MT_SMALLMACE:
|
||||||
|
|
@ -10978,6 +11010,11 @@ void P_RemoveMobj(mobj_t *mobj)
|
||||||
Obj_ShrinkGunRemoved(mobj);
|
Obj_ShrinkGunRemoved(mobj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mobj->type == MT_SPECIAL_UFO_PIECE)
|
||||||
|
{
|
||||||
|
Obj_UFOPieceRemoved(mobj);
|
||||||
|
}
|
||||||
|
|
||||||
mobj->health = 0; // Just because
|
mobj->health = 0; // Just because
|
||||||
|
|
||||||
// unlink from sector and block lists
|
// unlink from sector and block lists
|
||||||
|
|
@ -11541,13 +11578,12 @@ void P_SpawnPlayer(INT32 playernum)
|
||||||
}
|
}
|
||||||
else if (p->bot)
|
else if (p->bot)
|
||||||
{
|
{
|
||||||
/*
|
if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)
|
||||||
if (bonusgame || specialstage || boss)
|
|
||||||
{
|
{
|
||||||
// Bots should avoid
|
// Bots aren't supposed to be here.
|
||||||
p->spectator = true;
|
p->spectator = true;
|
||||||
}
|
}
|
||||||
*/
|
else
|
||||||
{
|
{
|
||||||
// No point in a spectating bot!
|
// No point in a spectating bot!
|
||||||
p->spectator = false;
|
p->spectator = false;
|
||||||
|
|
@ -12018,7 +12054,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
|
||||||
|
|
||||||
// in record attack, only spawn ring capsules
|
// in record attack, only spawn ring capsules
|
||||||
// (behavior can be inverted with the Extra flag, i.e. item capsule spawns and ring capsule does not)
|
// (behavior can be inverted with the Extra flag, i.e. item capsule spawns and ring capsule does not)
|
||||||
if (modeattacking
|
if (K_TimeAttackRules() == true
|
||||||
&& (!(mthing->args[2] & TMICF_INVERTTIMEATTACK) == !isRingCapsule))
|
&& (!(mthing->args[2] & TMICF_INVERTTIMEATTACK) == !isRingCapsule))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue