Merge branch 'boss' into 'master'

Battle-related cleanup + Boss API

See merge request KartKrew/Kart!556
This commit is contained in:
toaster 2022-03-21 23:44:54 +00:00
commit ee18d281bd
44 changed files with 1960 additions and 497 deletions

View file

@ -110,6 +110,7 @@ k_bot.c
k_botitem.c
k_botsearch.c
k_grandprix.c
k_boss.c
k_hud.c
k_terrain.c
k_brightmap.c

View file

@ -53,6 +53,7 @@
#include "k_pwrlv.h"
#include "k_bot.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "doomstat.h"
#include "s_sound.h" // sfx_syfail
@ -3647,11 +3648,12 @@ void SV_StopServer(void)
// called at singleplayer start and stopdemo
void SV_StartSinglePlayerServer(void)
{
INT32 lastgametype = gametype;
server = true;
netgame = false;
multiplayer = false;
if (modeattacking == ATTACKING_CAPSULES)
if ((modeattacking == ATTACKING_CAPSULES) || (bossinfo.boss == true))
{
G_SetGametype(GT_BATTLE);
}
@ -3660,6 +3662,9 @@ void SV_StartSinglePlayerServer(void)
G_SetGametype(GT_RACE);
}
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
// no more tic the game with this settings!
SV_StopServer();

View file

@ -71,6 +71,7 @@
// SRB2Kart
#include "k_grandprix.h"
#include "k_boss.h"
#include "doomstat.h"
#ifdef CMAKECONFIG
@ -928,6 +929,9 @@ void D_StartTitle(void)
// Reset GP
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
// Reset boss info
K_ResetBossInfo();
// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
maptol = 0;

View file

@ -56,6 +56,7 @@
#include "k_color.h"
#include "k_respawn.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "doomstat.h"
#include "deh_tables.h"
@ -2376,6 +2377,10 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
// Too lazy to change the input value for every instance of this function.......
pencoremode = grandprixinfo.encore;
}
else if (bossinfo.boss == true)
{
pencoremode = bossinfo.encore;
}
if (delay != 2)
{
@ -2573,7 +2578,6 @@ static void Command_Map_f(void)
INT32 newgametype = gametype;
boolean newencoremode = (cv_kartencore.value == 1);
boolean startgp = false;
INT32 d;
@ -2591,7 +2595,7 @@ static void Command_Map_f(void)
mustmodifygame = !(netgame || multiplayer) && !majormods;
if (mustmodifygame)
if (mustmodifygame && !option_force)
{
/* May want to be more descriptive? */
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
@ -2648,7 +2652,6 @@ static void Command_Map_f(void)
if (mustmodifygame && option_force)
{
G_SetGameModified(multiplayer, true);
startgp = true;
}
// new gametype value
@ -2690,6 +2693,15 @@ static void Command_Map_f(void)
}
}
}
else if (!Playing())
{
newresetplayers = true;
if (mapheaderinfo[newmapnum-1])
{
// 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;
}
}
// new encoremode value
if (option_encore)
@ -2703,67 +2715,7 @@ static void Command_Map_f(void)
}
}
if (startgp)
{
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
grandprixinfo.masterbots = false;
if (option_skill)
{
const char *masterstr = "Master";
const char *skillname = COM_Argv(option_skill + 1);
INT32 newskill = -1;
INT32 j;
if (!strcasecmp(masterstr, skillname))
{
newskill = KARTGP_MASTER;
}
else
{
for (j = 0; kartspeed_cons_t[j].strvalue; j++)
{
if (!strcasecmp(kartspeed_cons_t[j].strvalue, skillname))
{
newskill = (INT16)kartspeed_cons_t[j].value;
break;
}
}
if (!kartspeed_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;
}
if (!option_force && newgametype == gametype) // SRB2Kart
if (!option_force && newgametype == gametype && Playing()) // SRB2Kart
newresetplayers = false; // if not forcing and gametypes is the same
// don't use a gametype the map doesn't support
@ -2792,6 +2744,83 @@ static void Command_Map_f(void)
}
}
if (!(netgame || multiplayer))
{
if (newgametype == GT_BATTLE)
{
grandprixinfo.gp = false;
K_ResetBossInfo();
if (mapheaderinfo[newmapnum-1] &&
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_BOSS)
{
bossinfo.boss = true;
bossinfo.encore = newencoremode;
}
}
else // default GP
{
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
grandprixinfo.masterbots = false;
if (option_skill)
{
const char *masterstr = "Master";
const char *skillname = COM_Argv(option_skill + 1);
INT32 newskill = -1;
INT32 j;
if (!strcasecmp(masterstr, skillname))
{
newskill = KARTGP_MASTER;
}
else
{
for (j = 0; kartspeed_cons_t[j].strvalue; j++)
{
if (!strcasecmp(kartspeed_cons_t[j].strvalue, skillname))
{
newskill = (INT16)kartspeed_cons_t[j].value;
break;
}
}
if (!kartspeed_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;
grandprixinfo.initalize = true;
}
}
// Prevent warping to locked levels
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
@ -2858,7 +2887,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
else if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
if (!(gametyperules & GTR_CIRCUIT))
if (!(gametyperules & GTR_CIRCUIT) && !bossinfo.boss)
pencoremode = false;
skipprecutscene = ((flags & (1<<2)) != 0);
@ -4486,7 +4515,7 @@ UINT32 timelimitintics = 0;
static void TimeLimit_OnChange(void)
{
// Don't allow timelimit in Single Player/Co-Op/Race!
if (server && Playing() && cv_timelimit.value != 0 && !(gametyperules & GTR_TIMELIMIT))
if (server && Playing() && cv_timelimit.value != 0 && (bossinfo.boss || !(gametyperules & GTR_TIMELIMIT)))
{
CV_SetValue(&cv_timelimit, 0);
return;
@ -4541,10 +4570,12 @@ void D_GameTypeChanged(INT32 lastgametype)
CV_SetValue(&cv_pointlimit, pointlimits[gametype]);
}
}
/* -- no longer useful
else if (!multiplayer && !netgame)
{
G_SetGametype(GT_RACE);
}
*/
// reset timelimit and pointlimit in race/coop, prevent stupid cheats
if (server)
@ -4996,9 +5027,9 @@ void Command_Retry_f(void)
{
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
}
else if (grandprixinfo.gp == false)
else if (grandprixinfo.gp == false && bossinfo.boss == false)
{
CONS_Printf(M_GetText("This only works in Grand Prix.\n"));
CONS_Printf(M_GetText("This only works in Grand Prix or Mission Mode.\n"));
}
else
{

View file

@ -261,7 +261,7 @@ typedef enum
//}
// for kickstartaccel
#define ACCEL_KICKSTART 35
#define ACCEL_KICKSTART (TICRATE)
#define ITEMSCALE_NORMAL 0
#define ITEMSCALE_GROW 1
@ -494,6 +494,7 @@ typedef struct player_s
INT16 karmadelay;
tic_t overtimekarma; // time to live in overtime comeback
INT16 spheres;
tic_t spheredigestion;
SINT8 glanceDir; // Direction the player is trying to look backwards in

View file

@ -1631,9 +1631,9 @@ void readlevelheader(MYFILE *f, INT32 num)
else if (fastcmp(word, "TIMEATTACK") || fastcmp(word, "RECORDATTACK"))
{ // RECORDATTACK is an accepted alias
if (i || word2[0] == 'T' || word2[0] == 'Y')
mapheaderinfo[num-1]->menuflags |= LF2_TIMEATTACK;
mapheaderinfo[num-1]->menuflags &= ~LF2_NOTIMEATTACK;
else
mapheaderinfo[num-1]->menuflags &= ~LF2_TIMEATTACK;
mapheaderinfo[num-1]->menuflags |= LF2_NOTIMEATTACK;
}
else if (fastcmp(word, "VISITNEEDED"))
{

View file

@ -23,6 +23,7 @@
#include "i_sound.h" // musictype_t (for lua)
#include "g_state.h" // gamestate_t (for lua)
#include "r_data.h" // patchalphastyle_t
#include "k_boss.h" // spottype_t (for lua)
#include "deh_tables.h"
@ -5862,6 +5863,8 @@ const char *const MOBJEFLAG_LIST[] = {
"APPLYPMOMZ", // Platform movement
"TRACERANGLE", // Compute and trigger on mobj angle relative to tracer
"JUSTBOUNCEDWALL",
"DAMAGEHITLAG",
"SLOPELAUNCHED",
NULL
};
@ -6316,6 +6319,7 @@ struct int_const_s const INT_CONST[] = {
{"FRACUNIT",FRACUNIT},
{"FU" ,FRACUNIT},
{"FRACBITS",FRACBITS},
{"M_TAU_FIXED",M_TAU_FIXED},
// doomdef.h constants
{"TICRATE",TICRATE},
@ -6469,7 +6473,7 @@ struct int_const_s const INT_CONST[] = {
// And map flags
{"LF2_HIDEINMENU",LF2_HIDEINMENU},
{"LF2_HIDEINSTATS",LF2_HIDEINSTATS},
{"LF2_TIMEATTACK",LF2_TIMEATTACK},
{"LF2_NOTIMEATTACK",LF2_NOTIMEATTACK},
{"LF2_VISITNEEDED",LF2_VISITNEEDED},
// Emeralds
@ -6568,7 +6572,7 @@ struct int_const_s const INT_CONST[] = {
{"int_none",int_none},
{"int_race",int_race},
{"int_battle",int_battle},
{"int_timeattack",int_timeattack},
{"int_battletime", int_battletime},
// Jingles (jingletype_t)
{"JT_NONE",JT_NONE},
@ -6940,6 +6944,11 @@ struct int_const_s const INT_CONST[] = {
{"KSPIN_STUNG",KSPIN_STUNG},
{"KSPIN_EXPLOSION",KSPIN_EXPLOSION},
// spottype_t
{"SPOT_NONE",SPOT_NONE},
{"SPOT_WEAK",SPOT_WEAK},
{"SPOT_BUMP",SPOT_BUMP},
{NULL,0}
};

View file

@ -414,7 +414,7 @@ typedef struct
#define LF2_HIDEINMENU (1<<0) ///< Hide in the multiplayer menu
#define LF2_HIDEINSTATS (1<<1) ///< Hide in the statistics screen
#define LF2_TIMEATTACK (1<<2) ///< Show this map in Time Attack modes
#define LF2_NOTIMEATTACK (1<<2) ///< Hide this map in Time Attack modes
#define LF2_VISITNEEDED (1<<3) ///< Not available in Time Attack modes until you visit the level
extern mapheader_t* mapheaderinfo[NUMMAPS];
@ -500,15 +500,16 @@ extern INT32 timelimits[NUMGAMETYPES];
enum TypeOfLevel
{
// Gametypes
TOL_RACE = 0x0001, ///< Race
TOL_BATTLE = 0x0002, ///< Battle
TOL_RACE = 0x0001, ///< Race
TOL_BATTLE = 0x0002, ///< Battle
TOL_BOSS = 0x0004, ///< Boss (variant of battle, but forbidden)
// Modifiers
TOL_TV = 0x0100, ///< Midnight Channel specific: draw TV like overlay on HUD
TOL_TV = 0x0100 ///< Midnight Channel specific: draw TV like overlay on HUD
};
#define MAXTOL (1<<31)
#define NUMBASETOLNAMES (3)
#define NUMBASETOLNAMES (4)
#define NUMTOLNAMES (NUMBASETOLNAMES + NUMGAMETYPEFREESLOTS)
typedef struct

View file

@ -56,6 +56,7 @@
#include "k_color.h"
#include "k_respawn.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_bot.h"
#include "doomstat.h"
@ -2098,6 +2099,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT32 starpostnum;
INT32 exiting;
INT32 khudcardanimation;
INT16 totalring;
UINT8 laps;
UINT16 skincolor;
@ -2183,11 +2185,13 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
rings = ((gametyperules & GTR_SPHERES) ? 0 : 5);
spheres = 0;
kickstartaccel = 0;
khudfault = nocontrol = 0;
khudfault = 0;
nocontrol = 0;
laps = 0;
totalring = 0;
roundscore = 0;
exiting = 0;
khudcardanimation = 0;
starpostnum = 0;
xtralife = 0;
@ -2226,7 +2230,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
laps = players[player].laps;
totalring = players[player].totalring;
roundscore = players[player].roundscore;
exiting = players[player].exiting;
khudcardanimation = (exiting > 0) ? players[player].karthud[khud_cardanimation] : 0;
starpostnum = players[player].starpostnum;
xtralife = players[player].xtralife;
@ -2240,8 +2247,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
if (!(netgame || multiplayer))
pflags |= (players[player].pflags & (PF_GODMODE|PF_NOCLIP));
// Obliterate follower from existence
P_SetTarget(&players[player].follower, NULL);
if (!betweenmaps)
{
// Obliterate follower from existence (if valid memory)
P_SetTarget(&players[player].follower, NULL);
}
memcpy(&respawn, &players[player].respawn, sizeof (respawn));
@ -2271,6 +2281,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->starpostnum = starpostnum;
p->exiting = exiting;
p->karthud[khud_cardanimation] = khudcardanimation;
p->laps = laps;
p->totalring = totalring;
@ -2354,23 +2365,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
if (leveltime < starttime)
return;
if (p-players == consoleplayer)
{
if (mapmusflags & MUSIC_RELOADRESET)
{
strncpy(mapmusname, mapheaderinfo[gamemap-1]->musname, 7);
mapmusname[6] = 0;
mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK);
mapmusposition = mapheaderinfo[gamemap-1]->muspos;
mapmusresume = 0;
songcredit = true;
}
// This is in S_Start, but this was not here previously.
// if (RESETMUSIC)
// S_StopMusic();
S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
}
if (exiting)
return;
P_RestoreMusic(p);
@ -2665,7 +2661,7 @@ mapthing_t *G_FindMapStart(INT32 playernum)
{
// In platform gametypes, spawn in Co-op starts first
// Overriden by GTR_BATTLESTARTS.
if (gametyperules & GTR_BATTLESTARTS)
if (gametyperules & GTR_BATTLESTARTS && bossinfo.boss == false)
spawnpoint = G_FindBattleStartOrFallback(playernum);
else
spawnpoint = G_FindRaceStartOrFallback(playernum);
@ -2772,10 +2768,30 @@ void G_ExitLevel(void)
{
if (gamestate == GS_LEVEL)
{
if (grandprixinfo.gp == true && grandprixinfo.wonround != true)
UINT8 i;
boolean youlost = false;
if (bossinfo.boss == true)
{
UINT8 i;
youlost = true;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator && !players[i].bot)
{
if (players[i].bumpers > 0)
{
youlost = false;
break;
}
}
}
}
else if (grandprixinfo.gp == true)
{
youlost = (grandprixinfo.wonround != true);
}
if (youlost)
{
// You didn't win...
for (i = 0; i < MAXPLAYERS; i++)
@ -3004,6 +3020,7 @@ UINT32 gametypetol[NUMGAMETYPES] =
tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = {
{"RACE",TOL_RACE},
{"BATTLE",TOL_BATTLE},
{"BOSS",TOL_BOSS},
{"TV",TOL_TV},
{NULL, 0}
};
@ -3081,10 +3098,17 @@ boolean G_IsSpecialStage(INT32 mapnum)
//
boolean G_GametypeUsesLives(void)
{
if (modeattacking || metalrecording) // NOT in Record Attack
return false;
if (bossinfo.boss == true) // Fighting a boss?
{
return true;
}
if ((grandprixinfo.gp == true) // In Grand Prix
&& (gametype == GT_RACE) // NOT in bonus round
&& !G_IsSpecialStage(gamemap) // NOT in special stage
&& !(modeattacking || metalrecording)) // NOT in Record Attack
&& !G_IsSpecialStage(gamemap)) // NOT in special stage
{
return true;
}
@ -3565,7 +3589,7 @@ static void G_DoCompleted(void)
// a map of the proper gametype -- skip levels that don't support
// the current gametype. (Helps avoid playing boss levels in Race,
// for instance).
if (!modeattacking && grandprixinfo.gp == false)
if (!modeattacking && grandprixinfo.gp == false && bossinfo.boss == false)
{
if (nextmap >= 0 && nextmap < NUMMAPS)
{
@ -3722,7 +3746,7 @@ void G_NextLevel(void)
{
if (gamestate != GS_VOTING)
{
if ((cv_advancemap.value == 3) && grandprixinfo.gp == false && !modeattacking && !skipstats && (multiplayer || netgame))
if ((cv_advancemap.value == 3) && grandprixinfo.gp == false && bossinfo.boss == false && !modeattacking && !skipstats && (multiplayer || netgame))
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)

View file

@ -54,6 +54,7 @@
// SRB2Kart
#include "s_sound.h" // song credits
#include "k_kart.h"
#include "k_boss.h"
#include "k_color.h"
#include "k_hud.h"
#include "r_fps.h"
@ -2365,13 +2366,13 @@ static void HU_DrawRankings(void)
else
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, gametype_cons_t[gametype].strvalue);
if (gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT))
if ((gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT)) && !bossinfo.boss)
{
if ((gametyperules & GTR_TIMELIMIT) && cv_timelimit.value && timelimitintics > 0)
{
UINT32 timeval = (timelimitintics + starttime + 1 - leveltime);
if (timeval > timelimitintics+1)
timeval = timelimitintics;
timeval = timelimitintics+1;
timeval /= TICRATE;
if (leveltime <= (timelimitintics + starttime))

View file

@ -6784,7 +6784,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
4, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},

View file

@ -2,6 +2,7 @@
/// \brief SRB2Kart Battle Mode specific code
#include "k_battle.h"
#include "k_boss.h"
#include "k_kart.h"
#include "doomtype.h"
#include "doomdata.h"
@ -41,7 +42,17 @@ INT32 K_StartingBumperCount(void)
boolean K_IsPlayerWanted(player_t *player)
{
return (player->position == 1);
UINT8 i = 0, nump = 0, numfirst = 0;
for (; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
nump++;
if (players[i].position > 1)
continue;
numfirst++;
}
return ((numfirst < nump) && !player->spectator && (player->position == 1));
}
void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount)
@ -68,6 +79,9 @@ void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount)
pt->color = victim->skincolor;
else
pt->color = source->skincolor;
if (encoremode)
pt->renderflags ^= RF_HORIZONTALFLIP;
}
void K_CheckBumpers(void)
@ -75,7 +89,7 @@ void K_CheckBumpers(void)
UINT8 i;
UINT8 numingame = 0;
SINT8 winnernum = -1;
INT32 winnerscoreadd = 0;
UINT32 winnerscoreadd = 0, maxroundscore = 0;
boolean nobumpers = false;
if (!(gametyperules & GTR_BUMPERS))
@ -95,6 +109,11 @@ void K_CheckBumpers(void)
numingame++;
winnerscoreadd += players[i].roundscore;
if (players[i].roundscore > maxroundscore)
{
maxroundscore = players[i].roundscore;
}
if (players[i].bumpers <= 0) // if you don't have any bumpers, you're probably not a winner
{
nobumpers = true;
@ -107,7 +126,19 @@ void K_CheckBumpers(void)
winnerscoreadd -= players[i].roundscore;
}
if (numingame <= 1)
if (bossinfo.boss)
{
if (nobumpers)
{
for (i = 0; i < MAXPLAYERS; i++)
{
players[i].pflags |= PF_NOCONTEST;
P_DoPlayerExit(&players[i]);
}
}
return;
}
else if (numingame <= 1)
{
if (!battlecapsules)
{
@ -131,6 +162,8 @@ void K_CheckBumpers(void)
if (winnernum > -1 && playeringame[winnernum])
{
if ((players[winnernum].roundscore+winnerscoreadd) == maxroundscore)
winnerscoreadd++; // break ties if luigi wins by doing nothing
players[winnernum].roundscore += winnerscoreadd;
CONS_Printf(M_GetText("%s recieved %d point%s for winning!\n"), player_names[winnernum], winnerscoreadd, (winnerscoreadd == 1 ? "" : "s"));
}
@ -233,8 +266,6 @@ mobj_t *K_SpawnSphereBox(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 f
{
mobj_t *drop = P_SpawnMobj(x, y, z, MT_SPHEREBOX);
(void)amount;
P_InitAngle(drop, angle);
P_Thrust(drop,
FixedAngle(P_RandomFixed() * 180) + angle,
@ -293,6 +324,8 @@ void K_RunPaperItemSpawners(void)
const boolean overtime = (battleovertime.enabled >= 10*TICRATE);
tic_t interval = 8*TICRATE;
const boolean canmakeemeralds = true; //(!(battlecapsules || bossinfo.boss));
UINT32 emeraldsSpawned = 0;
UINT32 firstUnspawnedEmerald = 0;
@ -302,6 +335,12 @@ void K_RunPaperItemSpawners(void)
UINT8 pcount = 0;
INT16 i;
if (battlecapsules || bossinfo.boss)
{
// Gametype uses paper items, but this specific expression doesn't
return;
}
if (leveltime < starttime)
{
// Round hasn't started yet!
@ -355,14 +394,17 @@ void K_RunPaperItemSpawners(void)
}
}
for (i = 0; i < 7; i++)
if (canmakeemeralds)
{
UINT32 emeraldFlag = (1 << i);
if (!(emeraldsSpawned & emeraldFlag))
for (i = 0; i < 7; i++)
{
firstUnspawnedEmerald = emeraldFlag;
break;
UINT32 emeraldFlag = (1 << i);
if (!(emeraldsSpawned & emeraldFlag))
{
firstUnspawnedEmerald = emeraldFlag;
break;
}
}
}
@ -382,11 +424,14 @@ void K_RunPaperItemSpawners(void)
0, 0
);
K_SpawnSphereBox(
battleovertime.x, battleovertime.y, battleovertime.z + (128 * mapobjectscale * flip),
FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip,
10
);
if (gametyperules & GTR_SPHERES)
{
K_SpawnSphereBox(
battleovertime.x, battleovertime.y, battleovertime.z + (128 * mapobjectscale * flip),
FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip,
10
);
}
}
}
else
@ -428,15 +473,18 @@ void K_RunPaperItemSpawners(void)
return;
}
for (i = 0; i < 7; i++)
if (canmakeemeralds)
{
UINT32 emeraldFlag = (1 << i);
if (!(emeraldsSpawned & emeraldFlag))
for (i = 0; i < 7; i++)
{
firstUnspawnedEmerald = emeraldFlag;
starti = -1;
break;
UINT32 emeraldFlag = (1 << i);
if (!(emeraldsSpawned & emeraldFlag))
{
firstUnspawnedEmerald = emeraldFlag;
starti = -1;
break;
}
}
}
@ -471,12 +519,15 @@ void K_RunPaperItemSpawners(void)
}
else
{
drop = K_SpawnSphereBox(
spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip),
FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip,
10
);
K_FlipFromObject(drop, spotList[r]);
if (gametyperules & GTR_SPHERES)
{
drop = K_SpawnSphereBox(
spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip),
FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip,
10
);
K_FlipFromObject(drop, spotList[r]);
}
drop = K_CreatePaperItem(
spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip),
@ -689,41 +740,79 @@ void K_SetupMovingCapsule(mapthing_t *mt, mobj_t *mobj)
}
}
void K_SpawnBattleCapsules(void)
void K_SpawnPlayerBattleBumpers(player_t *p)
{
if (!p->mo || p->bumpers <= 0)
return;
{
INT32 i;
angle_t diff = FixedAngle(360*FRACUNIT/p->bumpers);
angle_t newangle = p->mo->angle;
mobj_t *bump;
for (i = 0; i < p->bumpers; i++)
{
bump = P_SpawnMobjFromMobj(p->mo,
P_ReturnThrustX(p->mo, newangle + ANGLE_180, 64*FRACUNIT),
P_ReturnThrustY(p->mo, newangle + ANGLE_180, 64*FRACUNIT),
0, MT_BATTLEBUMPER);
bump->threshold = i;
P_SetTarget(&bump->target, p->mo);
bump->angle = newangle;
bump->color = p->mo->color;
if (p->mo->renderflags & RF_DONTDRAW)
bump->renderflags |= RF_DONTDRAW;
else
bump->renderflags &= ~RF_DONTDRAW;
newangle += diff;
}
}
}
void K_BattleInit(void)
{
mapthing_t *mt;
size_t i;
if (battlecapsules)
return;
if (!(gametyperules & GTR_CAPSULES))
return;
if (modeattacking != ATTACKING_CAPSULES)
if ((gametyperules & GTR_CAPSULES) && !battlecapsules && !bossinfo.boss)
{
UINT8 n = 0;
mapthing_t *mt;
if (modeattacking != ATTACKING_CAPSULES)
{
UINT8 n = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
n++;
}
if (n > 1)
goto aftercapsules;
}
mt = mapthings;
for (i = 0; i < nummapthings; i++, mt++)
{
if (mt->type == mobjinfo[MT_BATTLECAPSULE].doomednum)
P_SpawnMapThing(mt);
}
battlecapsules = true;
}
aftercapsules:
if (gametyperules & GTR_BUMPERS)
{
INT32 maxbumpers = K_StartingBumperCount();
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
n++;
if (players[i].exiting)
return;
if (n > 1)
break;
if (!playeringame[i] || players[i].spectator)
continue;
players[i].bumpers = maxbumpers;
K_SpawnPlayerBattleBumpers(players+i);
}
if (n > 1)
return;
}
mt = mapthings;
for (i = 0; i < nummapthings; i++, mt++)
{
if (mt->type == mobjinfo[MT_BATTLECAPSULE].doomednum)
P_SpawnMapThing(mt);
}
battlecapsules = true;
}

View file

@ -28,6 +28,7 @@ UINT8 K_NumEmeralds(player_t *player);
void K_RunPaperItemSpawners(void);
void K_RunBattleOvertime(void);
void K_SetupMovingCapsule(mapthing_t *mt, mobj_t *mobj);
void K_SpawnBattleCapsules(void);
void K_SpawnPlayerBattleBumpers(player_t *p);
void K_BattleInit(void);
#endif

210
src/k_boss.c Normal file
View file

@ -0,0 +1,210 @@
// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2022 by Viv "toaster" Grannell
// Copyright (C) 2018-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 k_boss.c
/// \brief Boss battle game logic
#include "k_boss.h"
#include "doomdef.h"
#include "d_player.h"
#include "g_game.h"
#include "p_local.h"
#include "k_kart.h"
#include "s_sound.h"
#include "st_stuff.h"
#include "z_zone.h"
struct bossinfo bossinfo;
/*--------------------------------------------------
void K_ClearBossInfo(void)
See header file for description.
--------------------------------------------------*/
void K_ResetBossInfo(void)
{
Z_Free(bossinfo.enemyname);
Z_Free(bossinfo.subtitle);
memset(&bossinfo, 0, sizeof(struct bossinfo));
}
/*--------------------------------------------------
void K_BossInfoTicker(void)
See header file for description.
--------------------------------------------------*/
void K_BossInfoTicker(void)
{
UINT8 i;
if (bossinfo.boss == false)
return;
// Update healthbar data. (only if the hud is visible)
if (leveltime > (TICRATE/2)+3)
{
// Greater than the actual health?
if (bossinfo.visualbar > bossinfo.healthbar)
{
bossinfo.visualbar--;
// If the boss is dying, start shrinking the healthbar.
if (bossinfo.visualbar == 0)
bossinfo.barlen-= 2;
}
// Less than the actual health?
else if (bossinfo.visualbar < bossinfo.healthbar)
bossinfo.visualbar++;
// Continue to shrink the healthbar.
else if (bossinfo.barlen > 1 && bossinfo.barlen < BOSSHEALTHBARLEN)
bossinfo.barlen -= 2;
// Jitter timer.
if (bossinfo.visualbarimpact)
bossinfo.visualbarimpact--;
}
if (bossinfo.doweakspotsound != SPOT_NONE)
{
S_StartSound(NULL, sfx_mbs55); // may change for bump option
bossinfo.doweakspotsound = SPOT_NONE;
}
// Update weakspot data.
for (i = 0; i < NUMWEAKSPOTS; i++)
{
// Clean out invalid references.
if ((bossinfo.weakspots[i].spot && P_MobjWasRemoved(bossinfo.weakspots[i].spot)))
P_SetTarget(&bossinfo.weakspots[i].spot, NULL);
if (bossinfo.weakspots[i].spot == NULL)
continue;
// Damaged quickly? Make it disappear immediately (making sure to match the flashing).
if ((bossinfo.weakspots[i].spot->hitlag > 0) && (bossinfo.weakspots[i].time > TICRATE/2))
bossinfo.weakspots[i].time = (TICRATE/2) & ~(bossinfo.weakspots[i].time & 1);
// Handle counter.
if (!bossinfo.weakspots[i].time)
continue;
bossinfo.weakspots[i].time--;
// Get rid of concluded spots.
if (!bossinfo.weakspots[i].time && !bossinfo.weakspots[i].minimap)
P_SetTarget(&bossinfo.weakspots[i].spot, NULL);
}
}
/*--------------------------------------------------
void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t titlesound, fixed_t pinchmagnitude, UINT8 divisions)
See header file for description.
--------------------------------------------------*/
void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t titlesound, fixed_t pinchmagnitude, UINT8 divisions)
{
if (enemyname && enemyname[0])
{
if (bossinfo.enemyname)
Z_Free(bossinfo.enemyname);
bossinfo.enemyname = Z_StrDup(enemyname);
}
if (subtitle && subtitle[0])
{
if (bossinfo.subtitle)
Z_Free(bossinfo.subtitle);
bossinfo.subtitle = Z_StrDup(subtitle);
}
if (titlesound)
{
bossinfo.titlesound = titlesound;
}
bossinfo.barlen = BOSSHEALTHBARLEN;
K_UpdateBossHealthBar(FRACUNIT, 0);
if (pinchmagnitude > FRACUNIT)
pinchmagnitude = FRACUNIT;
else if (pinchmagnitude < 0)
pinchmagnitude = 0;
bossinfo.healthbarpinch = FixedMul(pinchmagnitude, BOSSHEALTHBARLEN*FRACUNIT)>>FRACBITS;
// we do this here so we can fudge our working a bit
if (divisions > 0)
{
if (divisions > (BOSSHEALTHBARLEN/2)) // megamanification
{
divisions = (BOSSHEALTHBARLEN/2);
}
bossinfo.visualdiv = FixedDiv(BOSSHEALTHBARLEN*FRACUNIT, divisions*FRACUNIT);
}
else
{
bossinfo.visualdiv = 0;
}
}
/*--------------------------------------------------
void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen)
See header file for description.
--------------------------------------------------*/
void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen)
{
if (magnitude > FRACUNIT)
magnitude = FRACUNIT;
else if (magnitude < 0)
magnitude = 0;
if (jitterlen > bossinfo.visualbarimpact)
bossinfo.visualbarimpact = jitterlen;
bossinfo.healthbar = FixedMul(magnitude, BOSSHEALTHBARLEN);
}
/*--------------------------------------------------
void K_DeclareWeakspot(mobj_t *weakspot, spottype_t spottype, boolean minimap)
See header file for description.
--------------------------------------------------*/
void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean minimap)
{
UINT8 i;
// First check whether the spot is already in the list and simply redeclaring weakness (for example, a vulnerable moment in the pattern).
for (i = 0; i < NUMWEAKSPOTS; i++)
if (bossinfo.weakspots[i].spot == spot)
break;
// If it's not redeclaring, check for an empty spot.
if (i == NUMWEAKSPOTS)
{
for (i = 0; i < NUMWEAKSPOTS; i++)
if (!bossinfo.weakspots[i].spot || P_MobjWasRemoved(bossinfo.weakspots[i].spot))
break;
}
// If you've crammed up the list with minimap entries (the only ones that will persist), give up.
if (i == NUMWEAKSPOTS)
return;
// OK, now we can slot in the weakspot data...
P_SetTarget(&bossinfo.weakspots[i].spot, spot);
bossinfo.weakspots[i].type = spottype;
bossinfo.weakspots[i].time = WEAKSPOTANIMTIME;
bossinfo.weakspots[i].color = color;
bossinfo.weakspots[i].minimap = minimap;
if (spottype && bossinfo.doweakspotsound != SPOT_WEAK)
{
bossinfo.doweakspotsound = spottype;
}
}

115
src/k_boss.h Normal file
View file

@ -0,0 +1,115 @@
// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2022 by Viv "toaster" Grannell
// Copyright (C) 2018-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 k_boss.h
/// \brief Boss battle game logic
#ifndef __K_BOSS__
#define __K_BOSS__
#include "doomdef.h"
#include "doomstat.h"
typedef enum
{
SPOT_NONE = 0,
SPOT_WEAK,
SPOT_BUMP,
} spottype_t;
#define NUMWEAKSPOTS 8
#define WEAKSPOTANIMTIME (3*TICRATE)
typedef struct weakspot_t
{
mobj_t *spot;
spottype_t type;
tic_t time;
UINT16 color;
boolean minimap;
} weakspot_t;
#define BOSSHEALTHBARLEN 110
extern struct bossinfo
{
boolean boss; ///< If true, then we are fighting a boss
UINT8 healthbar; ///< Actual health bar fill amount
UINT8 visualbar; ///< Tracks above, but with delay
fixed_t visualdiv; ///< How far apart health bar divisions should appear
tic_t visualbarimpact; ///< Bar jitter (on damage)
UINT8 healthbarpinch; ///< If actual health is lower than this, make it orange
UINT8 barlen; ///< The length of the bar (only reduced when a boss is deceased)
char *enemyname; ///< The name next to the bar
weakspot_t weakspots[NUMWEAKSPOTS]; ///< Array of weak spots (for minimap/object tracking)
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars
spottype_t doweakspotsound; ///< If nonzero, at least one weakspot was declared this tic
tic_t titleshow; ///< Show this many letters on the titlecard
sfxenum_t titlesound; ///< Sound to play when title typing
char *subtitle; ///< The subtitle under the titlecard
} bossinfo;
/*--------------------------------------------------
void K_ResetBossInfo(void);
Resets boss information to a clean slate.
--------------------------------------------------*/
void K_ResetBossInfo(void);
/*--------------------------------------------------
void K_ResetBossInfo(void);
Updates boss information and timers for this level tic.
--------------------------------------------------*/
void K_BossInfoTicker(void);
/*--------------------------------------------------
void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t titlesound, fixed_t pinchmagnitude, UINT8 divisions);
Initialises boss information for opponent spawn, including filling the health bar.
Input Arguments:-
enemyname - Zone memory string for HUD/titlecard name.
subtitle - Zone memory string for titlecard subtitle.
titlesound - Sound effect enum for titlecard typewriting.
pinchmagnitude - 0-FRACUNIT range for healthbar to display pinch status at.
divisions - # of segments on healthbar.
--------------------------------------------------*/
void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t titlesound, fixed_t pinchmagnitude, UINT8 divisions);
/*--------------------------------------------------
void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen);
Updates boss healthbar to a new magnitude.
Input Arguments:-
magnitude - 0-FRACUNIT range for healthbar to update to.
jitterlen - Duration healthbar should vibrate for.
--------------------------------------------------*/
void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen);
/*--------------------------------------------------
void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean minimap);
Updates the list of Weakspots for the HUD/minimap object tracking.
Input Arguments:-
spot - mobj_t reference.
spottype - Type of spot.
color - Color of associated UI elements.
minimap - If true, appear on minimap.
--------------------------------------------------*/
void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean minimap);
#endif

View file

@ -27,6 +27,7 @@
#include "m_random.h"
#include "r_things.h" // numskins
#include "k_race.h" // finishBeamLine
#include "k_boss.h"
/*--------------------------------------------------
@ -166,7 +167,7 @@ void K_UpdateMatchRaceBots(void)
}
}
if (difficulty == 0)
if (difficulty == 0 || bossinfo.boss == true)
{
wantedbots = 0;
}
@ -260,7 +261,10 @@ void K_UpdateMatchRaceBots(void)
--------------------------------------------------*/
boolean K_PlayerUsesBotMovement(player_t *player)
{
if (player->bot || player->exiting)
if (player->exiting)
return true;
if (player->bot)
return true;
return false;
@ -1040,6 +1044,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
|| player->mo->scale <= 1
|| player->playerstate == PST_DEAD
|| leveltime <= introtime
|| (player->exiting && !(gametyperules & GTR_CIRCUIT))
)
{
// No need to do anything else.

View file

@ -12,6 +12,30 @@
#include "doomdef.h" // Sink snipe print
#include "g_game.h" // Sink snipe print
angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2)
{
fixed_t momux, momuy;
angle_t test;
if (!(t1->flags & MF_PAPERCOLLISION))
{
return R_PointToAngle2(t1->x, t1->y, t2->x, t2->y)+ANGLE_90;
}
test = R_PointToAngle2(0, 0, t2->momx, t2->momy) + ANGLE_90 - t1->angle;
if (test > ANGLE_180)
test = t1->angle + ANGLE_180;
else
test = t1->angle;
// intentional way around - sine...
momuy = P_AproxDistance(t2->momx, t2->momy);
momux = t2->momx - P_ReturnThrustY(t2, test, 2*momuy);
momuy = t2->momy - P_ReturnThrustX(t2, test, 2*momuy);
return R_PointToAngle2(0, 0, momux, momuy);
}
boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
{
boolean damageitem = false;
@ -20,7 +44,7 @@ boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;
if (t1->health <= 0 || t2->health <= 0)
@ -61,11 +85,13 @@ boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
|| t2->type == MT_BALLHOG)
{
// Other Item Damage
angle_t bounceangle = K_GetCollideAngle(t1, t2);
S_StartSound(t2, t2->info->deathsound);
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_InstaThrust(t2, R_PointToAngle2(t1->x, t1->y, t2->x, t2->y)+ANGLE_90, 16*FRACUNIT);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH);
@ -92,11 +118,12 @@ boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
if (damageitem)
{
// This Item Damage
angle_t bounceangle = K_GetCollideAngle(t2, t1);
S_StartSound(t1, t1->info->deathsound);
P_KillMobj(t1, t2, t2, DMG_NORMAL);
P_SetObjectMomZ(t1, 8*FRACUNIT, false);
P_InstaThrust(t1, R_PointToAngle2(t2->x, t2->y, t1->x, t1->y)+ANGLE_90, 16*FRACUNIT);
P_InstaThrust(t1, bounceangle, 16*FRACUNIT);
}
if (sprung)
@ -114,7 +141,7 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;
if (t1->health <= 0 || t2->health <= 0)
@ -154,11 +181,13 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
|| t2->type == MT_BALLHOG)
{
// Other Item Damage
angle_t bounceangle = K_GetCollideAngle(t1, t2);
S_StartSound(t2, t2->info->deathsound);
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_InstaThrust(t2, R_PointToAngle2(t1->x, t1->y, t2->x, t2->y)+ANGLE_90, 16*FRACUNIT);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH);
@ -180,11 +209,13 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
if (damageitem)
{
// This Item Damage
angle_t bounceangle = K_GetCollideAngle(t2, t1);
S_StartSound(t1, t1->info->deathsound);
P_KillMobj(t1, t2, t2, DMG_NORMAL);
P_SetObjectMomZ(t1, 8*FRACUNIT, false);
P_InstaThrust(t1, R_PointToAngle2(t2->x, t2->y, t1->x, t1->y)+ANGLE_90, 16*FRACUNIT);
P_InstaThrust(t1, bounceangle, 16*FRACUNIT);
}
return true;
@ -270,7 +301,7 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2)
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;
if (t1->health <= 0 || t2->health <= 0)
@ -296,6 +327,8 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2)
|| t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD)
{
// Bomb death
angle_t bounceangle = K_GetCollideAngle(t1, t2);
P_KillMobj(t1, t2, t2, DMG_NORMAL);
// Other Item Damage
@ -303,7 +336,7 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2)
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_InstaThrust(t2, R_PointToAngle2(t1->x, t1->y, t2->x, t2->y)+ANGLE_90, 16*FRACUNIT);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
}
else if (t2->flags & MF_SHOOTABLE)
{
@ -346,7 +379,7 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;
if (t1->health <= 0 || t2->health <= 0)
@ -380,6 +413,8 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
|| t2->type == MT_BALLHOG)
{
// Other Item Damage
angle_t bounceangle = K_GetCollideAngle(t1, t2);
if (t2->eflags & MFE_VERTICALFLIP)
t2->z -= t2->height;
else
@ -389,7 +424,7 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
P_KillMobj(t2, t1, t1, DMG_NORMAL);
P_SetObjectMomZ(t2, 8*FRACUNIT, false);
P_InstaThrust(t2, R_PointToAngle2(t1->x, t1->y, t2->x, t2->y)+ANGLE_90, 16*FRACUNIT);
P_InstaThrust(t2, bounceangle, 16*FRACUNIT);
P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH);
@ -416,7 +451,7 @@ boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2)
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;
if (t2->player)
@ -535,8 +570,12 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
// (Pogo Spring damage is handled in head-stomping code)
if (gametyperules & GTR_BUMPERS)
{
t1Condition = (t1->player->sneakertimer > 0 && t1->player->flashing != 0);
t2Condition = (t2->player->sneakertimer > 0 && t2->player->flashing != 0);
t1Condition = ((t1->player->sneakertimer > 0)
&& !P_PlayerInPain(t1->player)
&& (t1->player->flashing == 0));
t2Condition = ((t2->player->sneakertimer > 0)
&& !P_PlayerInPain(t2->player)
&& (t2->player->flashing == 0));
if (t1Condition == true && t2Condition == false)
{

View file

@ -4,6 +4,7 @@
#include "doomtype.h"
#include "p_mobj.h"
angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2);
boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2);
boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2);
boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2);

View file

@ -11,6 +11,7 @@
/// \brief Grand Prix mode game logic & bot behaviors
#include "k_grandprix.h"
#include "k_boss.h"
#include "doomdef.h"
#include "d_player.h"
#include "g_game.h"
@ -595,6 +596,12 @@ boolean K_CanChangeRules(void)
return false;
}
if (bossinfo.boss == true)
{
// Don't cheat the boss!
return false;
}
if (modeattacking == true)
{
// Don't cheat the rules of Time Trials!

View file

@ -12,6 +12,7 @@
#include "k_hud.h"
#include "k_kart.h"
#include "k_battle.h"
#include "k_boss.h"
#include "k_color.h"
#include "k_director.h"
#include "screen.h"
@ -33,6 +34,7 @@
#include "s_sound.h"
#include "r_things.h"
#include "r_fps.h"
#include "m_random.h"
#define NUMPOSNUMS 10
#define NUMPOSFRAMES 7 // White, three blues, three reds
@ -53,6 +55,7 @@ static patch_t *kp_capsulesticker;
static patch_t *kp_capsulestickerwide;
static patch_t *kp_karmasticker;
static patch_t *kp_spheresticker;
static patch_t *kp_splitspheresticker;
static patch_t *kp_splitkarmabomb;
static patch_t *kp_timeoutsticker;
@ -164,6 +167,9 @@ static patch_t *kp_cpu;
static patch_t *kp_nametagstem;
static patch_t *kp_bossbar[8];
static patch_t *kp_bossret[4];
static patch_t *kp_trickcool[2];
void K_LoadKartHUDGraphics(void)
@ -187,6 +193,7 @@ void K_LoadKartHUDGraphics(void)
kp_capsulestickerwide = W_CachePatchName("K_STCAPW", PU_HUDGFX);
kp_karmasticker = W_CachePatchName("K_STKARM", PU_HUDGFX);
kp_spheresticker = W_CachePatchName("K_STBSMT", PU_HUDGFX);
kp_splitspheresticker = W_CachePatchName("K_SPBSMT", PU_HUDGFX);
kp_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX);
kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX);
@ -606,6 +613,20 @@ void K_LoadKartHUDGraphics(void)
kp_nametagstem = (patch_t *) W_CachePatchName("K_NAMEST", PU_HUDGFX);
sprintf(buffer, "K_BOSB0x");
for (i = 0; i < 8; i++)
{
buffer[7] = '0'+((i+1)%10);
kp_bossbar[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
}
sprintf(buffer, "K_BOSR0x");
for (i = 0; i < 4; i++)
{
buffer[7] = '0'+((i+1)%10);
kp_bossret[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
}
kp_trickcool[0] = W_CachePatchName("K_COOL1", PU_HUDGFX);
kp_trickcool[1] = W_CachePatchName("K_COOL2", PU_HUDGFX);
}
@ -759,14 +780,22 @@ void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du
if (options & V_SLIDEIN)
{
const tic_t length = TICRATE/2;
const tic_t length = TICRATE/4;
tic_t timer = lt_exitticker;
if (bossinfo.boss == true)
{
if (leveltime <= 3)
timer = 0;
else
timer = leveltime-3;
}
if (lt_exitticker < length)
if (timer < length)
{
boolean slidefromright = false;
const INT32 offsetAmount = (screenwidth * FRACUNIT) / length;
fixed_t offset = (screenwidth * FRACUNIT) - (lt_exitticker * offsetAmount);
const INT32 offsetAmount = (screenwidth * FRACUNIT/2) / length;
fixed_t offset = (screenwidth * FRACUNIT/2) - (timer * offsetAmount);
offset += FixedMul(offsetAmount, renderdeltatics);
offset /= FRACUNIT;
@ -1430,17 +1459,34 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
// TIME_Y = 6; // 6
tic_t worktime;
boolean dontdraw = false;
INT32 splitflags = 0;
if (!mode)
{
splitflags = V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP|V_SNAPTORIGHT|V_SPLITSCREEN;
if (cv_timelimit.value && timelimitintics > 0)
#ifndef TESTOVERTIMEINFREEPLAY
if (battlecapsules) // capsules override any time limit settings
;
else
#endif
if (bossinfo.boss == true)
;
else if (timelimitintics > 0 && (gametyperules & GTR_TIMELIMIT)) // TODO
{
if (drawtime >= timelimitintics)
{
if (((drawtime-timelimitintics)/TICRATE) & 1)
{
dontdraw = true;
}
drawtime = 0;
}
else
{
drawtime = timelimitintics - drawtime;
}
}
}
@ -1452,6 +1498,8 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
if (mode && !drawtime)
V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--"));
else if (dontdraw) // overtime flash
;
else if (worktime < 100) // 99:99:99 only
{
// zero minute
@ -1555,13 +1603,13 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
goto bademblem;
}
V_DrawRightAlignedString(workx, worky, splitflags, targettext);
V_DrawRightAlignedString(workx, worky, splitflags|V_6WIDTHSPACE, targettext);
workx -= 67;
V_DrawSmallScaledPatch(workx + 4, worky, splitflags, W_CachePatchName("NEEDIT", PU_CACHE));
break;
bademblem:
bademblem:
emblem = M_GetLevelEmblems(-1);
}
@ -1868,24 +1916,179 @@ static boolean K_drawKartPositionFaces(void)
return false;
}
static void K_drawBossHealthBar(void)
{
UINT8 i = 0, barstatus = 1, randlen = 0, darken = 0;
const INT32 startx = BASEVIDWIDTH - 23;
INT32 starty = BASEVIDHEIGHT - 25;
INT32 rolrand = 0;
boolean randsign = false;
if (bossinfo.barlen <= 1)
return;
// Entire bar juddering!
if (lt_exitticker < (TICRATE/2))
;
else if (bossinfo.visualbarimpact)
{
INT32 mag = min((bossinfo.visualbarimpact/4) + 1, 8);
if (bossinfo.visualbarimpact & 1)
starty -= mag;
else
starty += mag;
}
if ((lt_ticker >= lt_endtime) && bossinfo.enemyname)
{
if (lt_exitticker == 0)
{
rolrand = 5;
}
else if (lt_exitticker == 1)
{
rolrand = 7;
}
else
{
rolrand = 10;
}
V_DrawRightAlignedThinString(startx, starty-rolrand, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_6WIDTHSPACE, bossinfo.enemyname);
rolrand = 0;
}
// Used for colour and randomisation.
if (bossinfo.healthbar <= (bossinfo.visualdiv/FRACUNIT))
{
barstatus = 3;
}
else if (bossinfo.healthbar <= bossinfo.healthbarpinch)
{
barstatus = 2;
}
randlen = M_RandomKey(bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)))+1;
randsign = M_RandomChance(FRACUNIT/2);
// Right wing.
V_DrawScaledPatch(startx-1, starty, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_FLIP, kp_bossbar[0]);
// Draw the bar itself...
while (i < bossinfo.barlen)
{
V_DrawScaledPatch(startx-i, starty, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, kp_bossbar[1]);
if (i < bossinfo.visualbar)
{
randlen--;
if (!randlen)
{
randlen = M_RandomKey(bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)))+1;
if (barstatus > 1)
{
rolrand = M_RandomKey(barstatus)+1;
}
else
{
rolrand = 1;
}
if (randsign)
{
rolrand = -rolrand;
}
randsign = !randsign;
}
else
{
rolrand = 0;
}
if (lt_exitticker < (TICRATE/2))
;
else if ((bossinfo.visualbar - i) < (INT32)(bossinfo.visualbarimpact/8))
{
if (bossinfo.visualbarimpact & 1)
rolrand += (bossinfo.visualbar - i);
else
rolrand -= (bossinfo.visualbar - i);
}
if (bossinfo.visualdiv)
{
fixed_t work = 0;
if ((i+1) == bossinfo.visualbar)
darken = 1;
else
{
darken = 0;
// a hybrid fixed-int modulo...
while ((work/FRACUNIT) < bossinfo.visualbar)
{
if (work/FRACUNIT != i)
{
work += bossinfo.visualdiv;
continue;
}
darken = 1;
break;
}
}
}
V_DrawScaledPatch(startx-i, starty+rolrand, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, kp_bossbar[(2*barstatus)+darken]);
}
i++;
}
// Left wing.
V_DrawScaledPatch(startx-i, starty, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, kp_bossbar[0]);
}
static void K_drawKartEmeralds(void)
{
static const INT32 emeraldOffsets[7][2] = {
{34, 0},
{25, 8},
{43, 8},
{16, 0},
{52, 0},
{7, 8},
{61, 8}
static const INT32 emeraldOffsets[7][3] = {
{34, 0, 15},
{25, 8, 11},
{43, 8, 19},
{16, 0, 7},
{52, 0, 23},
{ 7, 8, 3},
{61, 8, 27}
};
const INT32 startx = BASEVIDWIDTH - 77 - 8;
const INT32 starty = BASEVIDHEIGHT - 29 - 8;
INT32 splitflags = V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN;
INT32 startx = BASEVIDWIDTH - 77;
INT32 starty = BASEVIDHEIGHT - 29;
INT32 i = 0, xindex = 0;
INT32 i;
{
if (r_splitscreen)
{
starty = (starty/2) - 8;
}
starty -= 8;
V_DrawScaledPatch(startx, starty, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, kp_rankemeraldback);
if (r_splitscreen < 2)
{
startx -= 8;
if (r_splitscreen == 1 && stplyr == &players[displayplayers[0]])
{
starty = 1;
}
V_DrawScaledPatch(startx, starty, V_HUDTRANS|splitflags, kp_rankemeraldback);
}
else
{
xindex = 2;
starty -= 15;
if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3...
{
startx = LAPS_X;
splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
}
else // else, that means we're P2 or P4.
{
startx = LAPS2_X + 1;
splitflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
}
}
}
for (i = 0; i < 7; i++)
{
@ -1904,16 +2107,16 @@ static void K_drawKartEmeralds(void)
colormap = R_GetTranslationColormap(TC_DEFAULT, emeraldColor, GTC_CACHE);
V_DrawMappedPatch(
startx + emeraldOffsets[i][0], starty + emeraldOffsets[i][1],
V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT,
startx + emeraldOffsets[i][xindex], starty + emeraldOffsets[i][1],
V_HUDTRANS|splitflags,
kp_rankemerald, colormap
);
if (whiteFlash == true)
{
V_DrawScaledPatch(
startx + emeraldOffsets[i][0], starty + emeraldOffsets[i][1],
V_HUDTRANSHALF|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT,
startx + emeraldOffsets[i][xindex], starty + emeraldOffsets[i][1],
V_HUDTRANSHALF|splitflags,
kp_rankemeraldflash
);
}
@ -2178,7 +2381,8 @@ static void K_drawKartLapsAndRings(void)
{
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|V_SLIDEIN|splitflags, faceprefix[stplyr->skin][FACE_MINIMAP], colormap);
V_DrawScaledPatch(fr+34, fy-10, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[(stplyr->lives % 10)]); // make sure this doesn't overflow
if (stplyr->lives >= 0)
V_DrawScaledPatch(fr+34, fy-10, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[(stplyr->lives % 10)]); // make sure this doesn't overflow OR underflow
}
}
else
@ -2216,7 +2420,8 @@ static void K_drawKartLapsAndRings(void)
{
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|V_SLIDEIN|splitflags, faceprefix[stplyr->skin][FACE_RANK], colormap);
V_DrawScaledPatch(LAPS_X+63, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow
if (stplyr->lives >= 0)
V_DrawScaledPatch(LAPS_X+63, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow OR underflow
}
}
}
@ -2226,7 +2431,6 @@ static void K_drawKartLapsAndRings(void)
static void K_drawKartAccessibilityIcons(INT32 fx)
{
INT32 fy = LAPS_Y-25;
UINT8 col = 0, i, wid, fil;
INT32 splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
//INT32 step = 1; -- if there's ever more than one accessibility icon
@ -2239,7 +2443,8 @@ static void K_drawKartAccessibilityIcons(INT32 fx)
}
else
{
fy += 4;
fx = LAPS_X+43;
fy = LAPS_Y;
if (!(stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]])) // If we are not P1 or P3...
{
splitflags ^= (V_SNAPTOLEFT|V_SNAPTORIGHT);
@ -2250,17 +2455,19 @@ static void K_drawKartAccessibilityIcons(INT32 fx)
if (stplyr->pflags & PF_KICKSTARTACCEL) // just KICKSTARTACCEL right now, maybe more later
{
fil = 7-(stplyr->kickstartaccel*7)/ACCEL_KICKSTART;
i = 7;
SINT8 col = 0, wid, fil, ofs;
UINT8 i = 7;
ofs = (stplyr->kickstartaccel == ACCEL_KICKSTART) ? 1 : 0;
fil = i-(stplyr->kickstartaccel*i)/ACCEL_KICKSTART;
V_DrawFill(fx+4, fy-1, 2, 1, 31|V_SLIDEIN|splitflags);
V_DrawFill(fx, (fy-1)+8, 10, 1, 31|V_SLIDEIN|splitflags);
V_DrawFill(fx+4, fy+ofs-1, 2, 1, 31|V_SLIDEIN|splitflags);
V_DrawFill(fx, (fy+ofs-1)+8, 10, 1, 31|V_SLIDEIN|splitflags);
while (i--)
{
wid = (i/2)+1;
V_DrawFill(fx+4-wid, fy+i, 2+(wid*2), 1, 31|V_SLIDEIN|splitflags);
if (fil)
V_DrawFill(fx+4-wid, fy+ofs+i, 2+(wid*2), 1, 31|V_SLIDEIN|splitflags);
if (fil > 0)
{
if (i < fil)
col = 23;
@ -2273,7 +2480,7 @@ static void K_drawKartAccessibilityIcons(INT32 fx)
col = 0;
else
col = 3;
V_DrawFill(fx+5-wid, fy+i, (wid*2), 1, col|V_SLIDEIN|splitflags);
V_DrawFill(fx+5-wid, fy+ofs+i, (wid*2), 1, col|V_SLIDEIN|splitflags);
}
//fx += step*12;
@ -2341,25 +2548,81 @@ static void K_drawBlueSphereMeter(void)
UINT8 numBars = min((sphere / 10), maxBars);
UINT8 colorIndex = (sphere * sizeof(segColors)) / (40 + 1);
INT32 x = LAPS_X + 25;
INT32 fx, fy;
UINT8 i;
INT32 splitflags = V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
INT32 flipflag = 0;
INT32 xstep = 15;
V_DrawScaledPatch(LAPS_X, LAPS_Y - 22, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN, kp_spheresticker);
// pain and suffering defined below
if (r_splitscreen < 2) // don't change shit for THIS splitscreen.
{
fx = LAPS_X;
fy = LAPS_Y-22;
V_DrawScaledPatch(fx, fy, splitflags|flipflag, kp_spheresticker);
}
else
{
xstep = 8;
if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3...
{
fx = LAPS_X-2;
fy = LAPS_Y;
}
else // else, that means we're P2 or P4.
{
fx = LAPS2_X+(SHORT(kp_splitspheresticker->width) - 10);
fy = LAPS2_Y;
splitflags ^= V_SNAPTOLEFT|V_SNAPTORIGHT;
flipflag = V_FLIP; // make the string right aligned and other shit
xstep = -xstep;
}
fy -= 16;
V_DrawScaledPatch(fx, fy, splitflags|flipflag, kp_splitspheresticker);
}
if (r_splitscreen < 2)
{
fx += 25;
}
else
{
fx += (flipflag) ? -18 : 13;
}
for (i = 0; i <= numBars; i++)
{
UINT8 segLen = 10;
UINT8 segLen = (r_splitscreen < 2) ? 10 : 5;
if (i == numBars)
{
segLen = (sphere % 10);
if (r_splitscreen < 2)
;
else
{
segLen = (segLen+1)/2; // offset so nonzero spheres shows up IMMEDIATELY
if (!segLen)
break;
if (flipflag)
fx += (5-segLen);
}
}
V_DrawFill(x, LAPS_Y - 16, segLen, 3, segColors[max(colorIndex-1, 0)] | V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN);
V_DrawFill(x, LAPS_Y - 15, segLen, 1, segColors[max(colorIndex-2, 0)] | V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN);
V_DrawFill(x, LAPS_Y - 13, segLen, 3, segColors[colorIndex] | V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN);
if (r_splitscreen < 2)
{
V_DrawFill(fx, fy + 6, segLen, 3, segColors[max(colorIndex-1, 0)] | splitflags);
V_DrawFill(fx, fy + 7, segLen, 1, segColors[max(colorIndex-2, 0)] | splitflags);
V_DrawFill(fx, fy + 9, segLen, 3, segColors[colorIndex] | splitflags);
}
else
{
V_DrawFill(fx, fy + 5, segLen, 1, segColors[max(colorIndex-1, 0)] | splitflags);
V_DrawFill(fx, fy + 6, segLen, 1, segColors[max(colorIndex-2, 0)] | splitflags);
V_DrawFill(fx, fy + 7, segLen, 2, segColors[colorIndex] | splitflags);
}
x += 15;
fx += xstep;
}
}
@ -2385,13 +2648,13 @@ static void K_drawKartBumpersOrKarma(void)
{
fx = LAPS_X;
fy = LAPS_Y;
splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom.
splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
}
else // else, that means we're P2 or P4.
{
fx = LAPS2_X;
fy = LAPS2_Y;
splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom
splitflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
flipflag = V_FLIP; // make the string right aligned and other shit
}
}
@ -2470,8 +2733,10 @@ static void K_drawKartBumpersOrKarma(void)
else
V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumpersticker, colormap);
// TODO BETTER HUD
V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d %d", stplyr->bumpers, maxbumper, stplyr->overtimekarma / TICRATE));
if (bossinfo.boss)
V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->bumpers, maxbumper));
else // TODO BETTER HUD
V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d %d", stplyr->bumpers, maxbumper, stplyr->overtimekarma / TICRATE));
}
}
}
@ -2675,7 +2940,9 @@ static boolean K_ShowPlayerNametag(player_t *p)
if (gametyperules & GTR_CIRCUIT)
{
if ((p->position < stplyr->position-2)
if ((p->position == 0)
|| (stplyr->position == 0)
|| (p->position < stplyr->position-2)
|| (p->position > stplyr->position+2))
{
return false;
@ -2771,6 +3038,43 @@ static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT8 cnum
V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), V_6WIDTHSPACE|V_ALLOWLOWERCASE|clr, player_names[p - players]);
}
typedef struct weakspotdraw_t
{
UINT8 i;
INT32 x;
INT32 y;
boolean candrawtag;
} weakspotdraw_t;
static void K_DrawWeakSpot(weakspotdraw_t *ws)
{
UINT8 *colormap;
UINT8 j = (bossinfo.weakspots[ws->i].type == SPOT_BUMP) ? 1 : 0;
tic_t flashtime = ~1; // arbitrary high even number
if (bossinfo.weakspots[ws->i].time < TICRATE)
{
if (bossinfo.weakspots[ws->i].time & 1)
return;
flashtime = bossinfo.weakspots[ws->i].time;
}
else if (bossinfo.weakspots[ws->i].time > (WEAKSPOTANIMTIME - TICRATE))
flashtime = WEAKSPOTANIMTIME - bossinfo.weakspots[ws->i].time;
if (flashtime & 1)
colormap = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
else
colormap = R_GetTranslationColormap(TC_RAINBOW, bossinfo.weakspots[ws->i].color, GTC_CACHE);
V_DrawFixedPatch(ws->x, ws->y, FRACUNIT, 0, kp_bossret[j], colormap);
if (!ws->candrawtag || flashtime & 1 || flashtime < TICRATE/2)
return;
V_DrawFixedPatch(ws->x, ws->y, FRACUNIT, 0, kp_bossret[j+1], colormap);
}
static void K_drawKartNameTags(void)
{
const fixed_t maxdistance = 8192*mapobjectscale;
@ -2819,6 +3123,81 @@ static void K_drawKartNameTags(void)
c.z = R_InterpolateFixed(stplyr->mo->old_z, stplyr->mo->z);
}
// Maybe shouldn't be handling this here... but the camera info is too good.
if (bossinfo.boss)
{
weakspotdraw_t weakspotdraw[NUMWEAKSPOTS];
UINT8 numdraw = 0;
boolean onleft = false;
for (i = 0; i < NUMWEAKSPOTS; i++)
{
trackingResult_t result;
vector3_t v;
if (bossinfo.weakspots[i].spot == NULL || P_MobjWasRemoved(bossinfo.weakspots[i].spot))
{
// No object
continue;
}
if (bossinfo.weakspots[i].time == 0 || bossinfo.weakspots[i].type == SPOT_NONE)
{
// not visible
continue;
}
v.x = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_x, bossinfo.weakspots[i].spot->x);
v.y = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_y, bossinfo.weakspots[i].spot->y);
v.z = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_z, bossinfo.weakspots[i].spot->z);
v.z += (bossinfo.weakspots[i].spot->height / 2);
K_ObjectTracking(&result, &v, cnum, 0);
if (result.onScreen == false)
{
continue;
}
weakspotdraw[numdraw].i = i;
weakspotdraw[numdraw].x = result.x;
weakspotdraw[numdraw].y = result.y;
weakspotdraw[numdraw].candrawtag = true;
for (j = 0; j < numdraw; j++)
{
if (abs(weakspotdraw[j].x - weakspotdraw[numdraw].x) > 50*FRACUNIT)
{
continue;
}
onleft = (weakspotdraw[j].x < weakspotdraw[numdraw].x);
if (abs((onleft ? -5 : 5)
+ weakspotdraw[j].y - weakspotdraw[numdraw].y) > 18*FRACUNIT)
{
continue;
}
if (weakspotdraw[j].x < weakspotdraw[numdraw].x)
{
weakspotdraw[j].candrawtag = false;
break;
}
weakspotdraw[numdraw].candrawtag = false;
break;
}
numdraw++;
}
for (i = 0; i < numdraw; i++)
{
K_DrawWeakSpot(&weakspotdraw[i]);
}
}
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *ntplayer = &players[i];
@ -3051,7 +3430,8 @@ static void K_drawKartMinimap(void)
patch_t *AutomapPic;
INT32 i = 0;
INT32 x, y;
INT32 minimaptrans, splitflags = (r_splitscreen == 3 ? 0 : (V_SLIDEIN|V_SNAPTORIGHT)); // flags should only be 0 when it's centered (4p split)
INT32 minimaptrans = cv_kartminimap.value;
INT32 splitflags = 0;
UINT8 skin = 0;
UINT8 *colormap = NULL;
SINT8 localplayers[4];
@ -3077,20 +3457,26 @@ static void K_drawKartMinimap(void)
else
return; // no pic, just get outta here
x = MINI_X - (AutomapPic->width/2);
y = MINI_Y - (AutomapPic->height/2);
if (r_splitscreen < 2) // 1/2P right aligned
{
splitflags = (V_SLIDEIN|V_SNAPTORIGHT);
}
else if (r_splitscreen == 3) // 4P centered
{
const tic_t length = TICRATE/2;
if (!lt_exitticker)
return;
minimaptrans = cv_kartminimap.value;
if (lt_exitticker < length)
minimaptrans = (((INT32)lt_exitticker)*minimaptrans)/((INT32)length);
if (!minimaptrans)
return;
}
// 3P lives in the middle of the bottom right player and shouldn't fade in OR slide
if (!minimaptrans)
return;
x = MINI_X - (AutomapPic->width/2);
y = MINI_Y - (AutomapPic->height/2);
minimaptrans = ((10-minimaptrans)<<FF_TRANSSHIFT);
splitflags |= minimaptrans;
@ -3100,7 +3486,6 @@ static void K_drawKartMinimap(void)
else
V_DrawScaledPatch(x, y, splitflags, AutomapPic);
if (r_splitscreen != 2)
{
splitflags &= ~minimaptrans;
splitflags |= V_HUDTRANSHALF;
@ -3250,8 +3635,38 @@ static void K_drawKartMinimap(void)
}
// draw our local players here, opaque.
splitflags &= ~V_HUDTRANSHALF;
splitflags |= V_HUDTRANS;
{
splitflags &= ~V_HUDTRANSHALF;
splitflags |= V_HUDTRANS;
}
// ...but first, any boss targets.
if (bossinfo.boss)
{
for (i = 0; i < NUMWEAKSPOTS; i++)
{
// exists at all?
if (bossinfo.weakspots[i].spot == NULL || P_MobjWasRemoved(bossinfo.weakspots[i].spot))
continue;
// shows on the minimap?
if (bossinfo.weakspots[i].minimap == false)
continue;
// in the flashing period?
if ((bossinfo.weakspots[i].time > (WEAKSPOTANIMTIME-(TICRATE/2))) && (bossinfo.weakspots[i].time & 1))
continue;
colormap = NULL;
if (bossinfo.weakspots[i].color)
colormap = R_GetTranslationColormap(TC_RAINBOW, bossinfo.weakspots[i].color, GTC_CACHE);
interpx = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_x, bossinfo.weakspots[i].spot->x);
interpy = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_y, bossinfo.weakspots[i].spot->y);
// temporary graphic?
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, colormap, AutomapPic);
}
}
for (i = 0; i < numlocalplayers; i++)
{
@ -3483,7 +3898,8 @@ static void K_drawKartStartCountdown(void)
}
else if (leveltime >= introtime && leveltime < starttime-(3*TICRATE))
{
K_drawKartStartBulbs();
if (bossinfo.boss == false)
K_drawKartStartBulbs();
}
else
{
@ -4277,7 +4693,7 @@ static void K_DrawWaypointDebugger(void)
void K_drawKartHUD(void)
{
boolean isfreeplay = false;
boolean islonesome = false;
boolean battlefullscreen = false;
boolean freecam = demo.freecam; //disable some hud elements w/ freecam
UINT8 i;
@ -4351,12 +4767,7 @@ void K_drawKartHUD(void)
if (LUA_HudEnabled(hud_time))
K_drawKartTimestamp(stplyr->realtime, TIME_X, TIME_Y, gamemap, 0);
if (!modeattacking)
{
// The top-four faces on the left
//if (LUA_HudEnabled(hud_minirankings))
isfreeplay = K_drawKartPositionFaces();
}
islonesome = K_drawKartPositionFaces();
}
if (!stplyr->spectator && !demo.freecam) // Bottom of the screen elements, don't need in spectate mode
@ -4375,52 +4786,70 @@ void K_drawKartHUD(void)
V_DrawTinyScaledPatch(x-54, y, snapflags|V_SLIDEIN, W_CachePatchName("TTKBANNR", PU_CACHE));
V_DrawTinyScaledPatch(x-54, y+25, snapflags|V_SLIDEIN, W_CachePatchName("TTKART", PU_CACHE));
}
else if (gametype == GT_RACE) // Race-only elements
else
{
// Draw the lap counter
if (LUA_HudEnabled(hud_gametypeinfo))
K_drawKartLapsAndRings();
if (isfreeplay)
;
else if (!modeattacking)
if (LUA_HudEnabled(hud_position))
{
// Draw the numerical position
if (LUA_HudEnabled(hud_position))
K_DrawKartPositionNum(stplyr->position);
if (bossinfo.boss)
{
K_drawBossHealthBar();
}
else if (gametype == GT_RACE) // Race-only elements (not currently gametyperuleable)
{
if (!islonesome)
{
// Draw the numerical position
K_DrawKartPositionNum(stplyr->position);
}
}
else if (gametype == GT_BATTLE) // Battle-only (ditto)
{
if (!freecam && !battlecapsules)
{
K_drawKartEmeralds();
}
}
}
else //if (!(demo.playback && hu_showscores))
if (LUA_HudEnabled(hud_gametypeinfo))
{
if (gametyperules & GTR_CIRCUIT)
{
K_drawKartLapsAndRings();
}
else if (gametyperules & GTR_BUMPERS)
{
K_drawKartBumpersOrKarma();
}
}
// Draw the speedometer and/or accessibility icons
if (cv_kartspeedometer.value && !r_splitscreen && (LUA_HudEnabled(hud_speedometer)))
{
K_drawKartSpeedometer();
}
else
{
K_drawKartAccessibilityIcons(0);
}
if (gametyperules & GTR_SPHERES)
{
K_drawBlueSphereMeter();
}
if (modeattacking && !bossinfo.boss)
{
// Draw the input UI
if (LUA_HudEnabled(hud_position))
K_drawInput();
}
}
else if (gametype == GT_BATTLE) // Battle-only
{
// Draw the hits left!
if (LUA_HudEnabled(hud_gametypeinfo))
K_drawKartBumpersOrKarma();
}
// Draw the speedometer and/or accessibility icons
if (cv_kartspeedometer.value && !r_splitscreen && (LUA_HudEnabled(hud_speedometer)))
{
K_drawKartSpeedometer();
}
else
{
K_drawKartAccessibilityIcons((r_splitscreen > 1) ? 0 : 8);
}
if (gametyperules & GTR_SPHERES)
{
K_drawBlueSphereMeter();
}
}
// Draw the countdowns after everything else.
if (leveltime >= introtime
if (starttime != introtime
&& leveltime >= introtime
&& leveltime < starttime+TICRATE)
{
K_drawKartStartCountdown();
@ -4458,7 +4887,7 @@ void K_drawKartHUD(void)
V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem);
// Draw FREE PLAY.
if (isfreeplay && !stplyr->spectator)
if (islonesome && !modeattacking && !bossinfo.boss && !stplyr->spectator)
{
if (LUA_HudEnabled(hud_freeplay))
K_drawKartFreePlay();
@ -4511,9 +4940,4 @@ void K_drawKartHUD(void)
K_DrawWaypointDebugger();
K_DrawDirectorDebugger();
if (gametype == GT_BATTLE)
{
K_drawKartEmeralds();
}
}

View file

@ -6,6 +6,7 @@
#include "k_kart.h"
#include "k_battle.h"
#include "k_boss.h"
#include "k_pwrlv.h"
#include "k_color.h"
#include "k_respawn.h"
@ -56,49 +57,52 @@ void K_TimerInit(void)
UINT8 i;
UINT8 numPlayers = 0;//, numspec = 0;
for (i = 0; i < MAXPLAYERS; i++)
if (!bossinfo.boss)
{
if (!playeringame[i])
for (i = 0; i < MAXPLAYERS; i++)
{
continue;
if (!playeringame[i])
{
continue;
}
if (players[i].spectator == true)
{
//numspec++;
continue;
}
numPlayers++;
}
if (players[i].spectator == true)
if (numPlayers >= 2)
{
//numspec++;
continue;
rainbowstartavailable = true;
}
else
{
rainbowstartavailable = false;
}
numPlayers++;
}
// No intro in Record Attack / 1v1
// Leave unset for the value in K_TimerReset
if (numPlayers > 2)
{
introtime = (108) + 5; // 108 for rotation, + 5 for white fade
}
if (numPlayers >= 2)
{
rainbowstartavailable = true;
}
else
{
rainbowstartavailable = false;
}
numbulbs = 5;
// No intro in Record Attack / 1v1
// Leave unset for the value in K_TimerReset
if (numPlayers > 2)
{
introtime = (108) + 5; // 108 for rotation, + 5 for white fade
if (numPlayers > 2)
{
numbulbs += (numPlayers-2);
}
starttime = (introtime + (3*TICRATE)) + ((2*TICRATE) + (numbulbs * bulbtime)); // Start countdown time, + buffer time
}
numbulbs = 5;
if (numPlayers > 2)
{
numbulbs += (numPlayers-2);
}
starttime = (introtime + (3*TICRATE)) + ((2*TICRATE) + (numbulbs * bulbtime)); // Start countdown time, + buffer time
// NOW you can try to spawn in the Battle capsules, if there's not enough players for a match
K_SpawnBattleCapsules();
K_BattleInit();
//CONS_Printf("numbulbs set to %d (%d players, %d spectators) on tic %d\n", numbulbs, numPlayers, numspec, leveltime);
}
@ -271,6 +275,9 @@ boolean K_IsPlayerLosing(player_t *player)
if (battlecapsules && player->bumpers <= 0)
return true; // DNF in break the capsules
if (bossinfo.boss)
return (player->bumpers <= 0); // anything short of DNF is COOL
if (player->position == 1)
return false;
@ -1043,13 +1050,20 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
}
else if (gametype == GT_BATTLE)
{
if (mashed && (modeattacking || cv_banana.value)) // ANY mashed value? You get a banana.
if (mashed && (modeattacking || bossinfo.boss || cv_banana.value)) // ANY mashed value? You get a banana.
{
K_KartGetItemResult(player, KITEM_BANANA);
player->karthud[khud_itemblinkmode] = 1;
if (P_IsDisplayPlayer(player))
S_StartSound(NULL, sfx_itrolm);
}
else if (bossinfo.boss)
{
K_KartGetItemResult(player, KITEM_ORBINAUT);
player->karthud[khud_itemblinkmode] = 0;
if (P_IsDisplayPlayer(player))
S_StartSound(NULL, sfx_itrolf);
}
else
{
if (modeattacking || cv_tripleorbinaut.value) // Waited patiently? You get Orbinaut x3!
@ -3545,7 +3559,11 @@ void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers)
player->karmadelay = comebacktime;
if (netgame)
if (bossinfo.boss)
{
P_DoTimeOver(player);
}
else if (netgame)
{
CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]);
}
@ -5578,7 +5596,7 @@ void K_DropHnextList(player_t *player, boolean keepshields)
dropwork->angle = work->angle;
P_SetScale(dropwork, work->scale);
dropwork->destscale = work->destscale;
dropwork->destscale = K_ItemScaleForPlayer(player); //work->destscale;
dropwork->scalespeed = work->scalespeed;
dropwork->flags |= MF_NOCLIPTHING;
@ -5618,6 +5636,17 @@ void K_DropHnextList(player_t *player, boolean keepshields)
if (orbit) // splay out
{
if (dropwork->destscale > work->destscale)
{
fixed_t radius = FixedMul(work->info->radius, dropwork->destscale);
radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(radius, radius); // mobj's distance from its Target, or Radius.
dropwork->flags |= MF_NOCLIPTHING;
work->momx = FixedMul(FINECOSINE(work->angle>>ANGLETOFINESHIFT), radius);
work->momy = FixedMul(FINESINE(work->angle>>ANGLETOFINESHIFT), radius);
P_MoveOrigin(dropwork, player->mo->x + work->momx, player->mo->y + work->momy, player->mo->z);
dropwork->flags &= ~MF_NOCLIPTHING;
}
dropwork->flags2 |= MF2_AMBUSH;
dropwork->z += flip;
@ -7032,21 +7061,57 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
else if (player->rings < -20)
player->rings = -20;
if (player->spheres > 40)
player->spheres = 40;
// where's the < 0 check? see below the following block!
if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0))
{
// Deplete 1 every tic when removed from the game.
player->spheres--;
player->spheredigestion = 0;
}
else
{
// Deplete 1 every second when playing.
if ((leveltime % TICRATE) == 0)
player->spheres--;
tic_t spheredigestion = TICRATE; // Base rate of 1 every second when playing.
tic_t digestionpower = ((10 - player->kartspeed) + (10 - player->kartweight))-1; // 1 to 17
// currently 0-34
digestionpower = ((player->spheres*digestionpower)/20);
if (digestionpower >= spheredigestion)
{
spheredigestion = 1;
}
else
{
spheredigestion -= digestionpower;
}
if ((player->spheres > 0) && (player->spheredigestion > 0))
{
// If you got a massive boost in spheres, catch up digestion as necessary.
if (spheredigestion < player->spheredigestion)
{
player->spheredigestion = (spheredigestion + player->spheredigestion)/2;
}
player->spheredigestion--;
if (player->spheredigestion == 0)
{
player->spheres--;
player->spheredigestion = spheredigestion;
}
}
else
{
player->spheredigestion = spheredigestion;
}
}
if (player->spheres > 40)
player->spheres = 40;
else if (player->spheres < 0)
// where's the > 40 check? see above the previous block!
if (player->spheres < 0)
player->spheres = 0;
if (comeback == false || !(gametyperules & GTR_KARMA) || (player->pflags & PF_ELIMINATED))
@ -7177,7 +7242,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_TIMEOVER);
}
if ((battleovertime.enabled >= 10*TICRATE) && !(player->pflags & PF_ELIMINATED))
if ((battleovertime.enabled >= 10*TICRATE) && !(player->pflags & PF_ELIMINATED) && !player->exiting)
{
fixed_t distanceToBarrier = 0;
@ -8387,7 +8452,10 @@ void K_StripOther(player_t *player)
player->roulettetype = 0;
player->invincibilitytimer = 0;
K_RemoveGrowShrink(player);
if (player->growshrinktimer)
{
K_RemoveGrowShrink(player);
}
if (player->eggmanexplode)
{

View file

@ -9,6 +9,7 @@
#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems
#include "p_tick.h" // leveltime
#include "k_grandprix.h"
#include "k_boss.h"
// Online rankings for the main gametypes.
// This array is saved to the gamedata.
@ -30,7 +31,7 @@ SINT8 K_UsingPowerLevels(void)
{
SINT8 pt = PWRLV_DISABLED;
if (!cv_kartusepwrlv.value || !(netgame || (demo.playback && demo.netgame)) || grandprixinfo.gp == true)
if (!cv_kartusepwrlv.value || !(netgame || (demo.playback && demo.netgame)) || grandprixinfo.gp == true || bossinfo.boss == true)
{
return PWRLV_DISABLED;
}

View file

@ -28,6 +28,8 @@
#include "console.h"
#include "k_kart.h" // SRB2Kart
#include "k_battle.h"
#include "k_boss.h"
#include "k_collide.h"
#include "k_color.h"
#include "k_hud.h"
#include "d_netcmd.h" // IsPlayerAdmin
@ -2925,8 +2927,9 @@ static int lib_sSoundPlaying(lua_State *L)
INLEVEL
if (id >= NUMSFX)
return luaL_error(L, "sfx %d out of range (0 - %d)", id, NUMSFX-1);
if (!GetValidSoundOrigin(L, &origin))
return LUA_ErrInvalid(L, "mobj_t/sector_t");
if (!lua_isnil(L, 1))
if (!GetValidSoundOrigin(L, &origin))
return LUA_ErrInvalid(L, "mobj_t/sector_t");
lua_pushboolean(L, S_SoundPlaying(origin, id));
return 1;
}
@ -3811,6 +3814,66 @@ static int lib_kGetItemPatch(lua_State *L)
return 1;
}
static int lib_kGetCollideAngle(lua_State *L)
{
mobj_t *t1 = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
mobj_t *t2 = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
//HUDSAFE
if (!t1)
return LUA_ErrInvalid(L, "mobj_t");
if (!t2)
return LUA_ErrInvalid(L, "mobj_t");
lua_pushinteger(L, K_GetCollideAngle(t1, t2));
return 1;
}
static int lib_kAddHitLag(lua_State *L)
{
mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
tic_t tics = (tic_t)luaL_checkinteger(L, 2);
boolean fromdamage = lua_opttrueboolean(L, 3);
NOHUD
if (!mo)
return LUA_ErrInvalid(L, "mobj_t");
K_AddHitLag(mo, tics, fromdamage);
return 0;
}
static int lib_kInitBossHealthBar(lua_State *L)
{
const char *enemyname = luaL_checkstring(L, 1);
const char *subtitle = luaL_checkstring(L, 2);
sfxenum_t titlesound = luaL_checkinteger(L, 3);
fixed_t pinchmagnitude = luaL_checkfixed(L, 4);
UINT8 divisions = (UINT8)luaL_checkinteger(L, 5);
NOHUD
K_InitBossHealthBar(enemyname, subtitle, titlesound, pinchmagnitude, divisions);
return 0;
}
static int lib_kUpdateBossHealthBar(lua_State *L)
{
fixed_t magnitude = luaL_checkfixed(L, 1);
tic_t jitterlen = (tic_t)luaL_checkinteger(L, 2);
NOHUD
K_UpdateBossHealthBar(magnitude, jitterlen);
return 0;
}
static int lib_kDeclareWeakspot(lua_State *L)
{
mobj_t *spot = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
spottype_t spottype = luaL_checkinteger(L, 2);
UINT16 color = luaL_checkinteger(L, 3);
boolean minimap = lua_optboolean(L, 4);
NOHUD
if (!spot)
return LUA_ErrInvalid(L, "mobj_t");
K_DeclareWeakspot(spot, spottype, color, minimap);
return 0;
}
static luaL_Reg lib[] = {
{"print", lib_print},
{"chatprint", lib_chatprint},
@ -4081,6 +4144,14 @@ static luaL_Reg lib[] = {
{"K_GetKartFlashing",lib_kGetKartFlashing},
{"K_GetItemPatch",lib_kGetItemPatch},
{"K_GetCollideAngle",lib_kGetCollideAngle},
{"K_AddHitLag",lib_kAddHitLag},
// k_boss
{"K_InitBossHealthBar", lib_kInitBossHealthBar},
{"K_UpdateBossHealthBar", lib_kUpdateBossHealthBar},
{"K_DeclareWeakspot", lib_kDeclareWeakspot},
{NULL, NULL}
};

View file

@ -372,6 +372,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->karmadelay);
else if (fastcmp(field,"spheres"))
lua_pushinteger(L, plr->spheres);
else if (fastcmp(field,"spheredigestion"))
lua_pushinteger(L, plr->spheredigestion);
else if (fastcmp(field,"pflags"))
lua_pushinteger(L, plr->pflags);
else if (fastcmp(field,"panim"))
@ -712,6 +714,8 @@ static int player_set(lua_State *L)
plr->karmadelay = luaL_checkinteger(L, 3);
else if (fastcmp(field,"spheres"))
plr->spheres = luaL_checkinteger(L, 3);
else if (fastcmp(field,"spheredigestion"))
plr->spheredigestion = luaL_checkinteger(L, 3);
else if (fastcmp(field,"kartspeed"))
plr->kartspeed = luaL_checkinteger(L, 3);
else if (fastcmp(field,"kartweight"))

View file

@ -300,6 +300,12 @@ int LUA_PushGlobals(lua_State *L, const char *word)
} else if (fastcmp(word,"leveltime")) {
lua_pushinteger(L, leveltime);
return 1;
} else if (fastcmp(word,"introtime")) {
lua_pushinteger(L, introtime);
return 1;
} else if (fastcmp(word,"starttime")) {
lua_pushinteger(L, starttime);
return 1;
} else if (fastcmp(word,"defrosting")) {
lua_pushinteger(L, hook_defrosting);
return 1;

View file

@ -440,7 +440,7 @@ UINT8 M_GotLowEnoughTime(INT32 tictime)
for (i = 0; i < NUMMAPS; ++i)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_TIMEATTACK))
if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & LF2_NOTIMEATTACK))
continue;
if (!mainrecords[i] || !mainrecords[i]->time)

View file

@ -33,6 +33,10 @@
unit used as fixed_t
*/
#if (FRACBITS == 16)
#define M_TAU_FIXED 411769
#endif
typedef INT32 fixed_t;
/*!

View file

@ -154,7 +154,8 @@ typedef enum
LLM_CREATESERVER,
LLM_LEVELSELECT,
LLM_TIMEATTACK,
LLM_BREAKTHECAPSULES
LLM_BREAKTHECAPSULES,
LLM_BOSS
} levellist_mode_t;
levellist_mode_t levellistmode = LLM_CREATESERVER;
@ -855,7 +856,7 @@ enum
{
spgrandprix,
sptimeattack,
spbreakthecapsules
spbreakthecapsules,
};
// Single Player Load Game
@ -1747,7 +1748,7 @@ inline static void M_GetGametypeColor(void)
else
gt = gametype;
if (gt == GT_BATTLE)
if (gt == GT_BATTLE || levellistmode == LLM_BOSS)
{
highlightflags = V_REDMAP;
warningflags = V_ORANGEMAP;
@ -4360,13 +4361,16 @@ static void M_PatchSkinNameTable(void)
//
// M_PrepareCupList
//
static void M_PrepareCupList(void)
static boolean M_PrepareCupList(void)
{
cupheader_t *cup = kartcupheaders;
INT32 i = 0;
memset(dummygpcup_cons_t, 0, sizeof (dummygpcup_cons_t));
if (cup == NULL)
return false;
while (cup != NULL)
{
dummygpcup_cons_t[i].strvalue = cup->name;
@ -4384,6 +4388,8 @@ static void M_PrepareCupList(void)
}
CV_SetValue(&cv_dummygpcup, 1); // This causes crash sometimes?!
return true;
}
// Call before showing any level-select menus
@ -4405,7 +4411,7 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt)
{
// Random map!
if (mapnum == -1)
return (gamestate != GS_TIMEATTACK && !modeattacking);
return (levellistmode == LLM_CREATESERVER);
// Does the map exist?
if (!mapheaderinfo[mapnum])
@ -4440,7 +4446,7 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt)
return true;*/
case LLM_TIMEATTACK:
case LLM_BREAKTHECAPSULES:
if (!(mapheaderinfo[mapnum]->menuflags & LF2_TIMEATTACK))
if (mapheaderinfo[mapnum]->menuflags & LF2_NOTIMEATTACK)
return false;
if ((levellistmode == LLM_TIMEATTACK && !(mapheaderinfo[mapnum]->typeoflevel & TOL_RACE))
@ -4460,6 +4466,13 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt)
return false;
return true;
case LLM_BOSS:
if (!(mapheaderinfo[mapnum]->typeoflevel & TOL_BOSS))
return false;
return true;
default:
return false;
}
@ -4932,8 +4945,12 @@ static boolean M_AddonsRefresh(void)
return true;
}
#ifdef DEVELOP
prevmajormods = majormods;
#else
if (!majormods && prevmajormods)
prevmajormods = false;
#endif
if ((refreshdirmenu & REFRESHDIR_ADDFILE) || (majormods && !prevmajormods))
{
@ -6336,7 +6353,7 @@ static void M_RetryResponse(INT32 ch)
static void M_Retry(INT32 choice)
{
(void)choice;
M_StartMessage(M_GetText("Start this race over?\n\n(Press 'Y' to confirm)\n"), FUNCPTRCAST(M_RetryResponse), MM_YESNO);
M_StartMessage(va("Start this %s over?\n\n(Press 'Y' to confirm)\n", (gametyperules & GTR_CIRCUIT) ? "race" : "battle"),FUNCPTRCAST(M_RetryResponse),MM_YESNO);
}
static void M_SelectableClearMenus(INT32 choice)
@ -7633,7 +7650,7 @@ static void M_DrawLevelStats(void)
for (i = 0; i < NUMMAPS; i++)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_TIMEATTACK))
if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & LF2_NOTIMEATTACK))
continue;
if (!mainrecords[i] || mainrecords[i]->time <= 0)
@ -7706,8 +7723,12 @@ static void M_HandleLevelStats(INT32 choice)
static void M_GrandPrixTemp(INT32 choice)
{
(void)choice;
if (!M_PrepareCupList())
{
M_StartMessage(M_GetText("No cups found for Grand Prix.\n"),NULL,MM_NOTHING);
return;
}
M_PatchSkinNameTable();
M_PrepareCupList();
M_SetupNextMenu(&SP_GrandPrixTempDef);
}
@ -8804,14 +8825,14 @@ static INT32 M_FindFirstMap(INT32 gtype)
{
INT32 i;
if (mapheaderinfo[gamemap] && (mapheaderinfo[gamemap]->typeoflevel & gtype))
if (mapheaderinfo[gamemap] && (mapheaderinfo[gamemap]->typeoflevel & gametypetol[gtype]))
return gamemap;
for (i = 0; i < NUMMAPS; i++)
{
if (!mapheaderinfo[i])
continue;
if (!(mapheaderinfo[i]->typeoflevel & gtype))
if (!(mapheaderinfo[i]->typeoflevel & gametypetol[gtype]))
continue;
return i + 1;
}

View file

@ -9429,7 +9429,7 @@ void A_ForceWin(mobj_t *actor)
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && ((players[i].mo && players[i].mo->health)
|| ((netgame || multiplayer) && players[i].lives)))
&& !(players[i].pflags & PF_NOCONTEST)))
break;
}
@ -11086,14 +11086,10 @@ void A_Boss5Jump(mobj_t *actor)
if (!actor->tracer)
return; // Don't even bother if we've got nothing to aim at.
// Look up actor's current gravity situation
if (actor->subsector->sector->gravity)
g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
else
g = gravity;
g = FixedMul(gravity, mapobjectscale);
// Look up distance between actor and its tracer
x = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
x = FixedHypot(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
// Look up height difference between actor and its tracer
y = actor->tracer->z - actor->z;
@ -13299,7 +13295,17 @@ void A_ItemPop(mobj_t *actor)
remains->flags = actor->flags; // Transfer flags
remains->flags2 = actor->flags2; // Transfer flags2
remains->fuse = actor->fuse; // Transfer respawn timer
remains->threshold = (actor->threshold == 70 ? 70 : (actor->threshold == 69 ? 69 : 68));
remains->cvmem = leveltime;
remains->threshold = actor->threshold;
if (remains->threshold != 69 && remains->threshold != 70)
{
remains->threshold = 68;
}
// To insure this information doesn't have to be rediscovered every time you look at this function...
// A threshold of 0 is for a "living", ordinary random item.
// 68 means regular popped random item debris.
// 69 used to mean old Karma Item behaviour (now you can replicate this with MF2_DONTRESPAWN).
// 70 is a powered up Overtime item.
remains->skin = NULL;
remains->spawnpoint = actor->spawnpoint;
@ -13315,7 +13321,8 @@ void A_ItemPop(mobj_t *actor)
remains->flags2 &= ~MF2_AMBUSH;
if ((gametyperules & GTR_BUMPERS) && (actor->threshold != 69 && actor->threshold != 70))
// Here at mapload in battle?
if ((gametyperules & GTR_BUMPERS) && (actor->flags2 & MF2_BOSSNOTRAP))
numgotboxes++;
P_RemoveMobj(actor);

View file

@ -34,6 +34,7 @@
#include "k_battle.h"
#include "k_pwrlv.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_respawn.h"
#include "p_spec.h"
@ -633,6 +634,9 @@ void P_CheckTimeLimit(void)
if (!(gametyperules & GTR_TIMELIMIT))
return;
if (bossinfo.boss == true)
return;
if (leveltime < (timelimitintics + starttime))
return;
@ -732,6 +736,9 @@ void P_CheckPointLimit(void)
if (!(gametyperules & GTR_POINTLIMIT))
return;
if (bossinfo.boss == true)
return;
// pointlimit is nonzero, check if it's been reached by this player
if (G_GametypeHasTeams())
{

View file

@ -37,6 +37,7 @@
// SRB2kart
#include "k_kart.h"
#include "k_battle.h"
#include "k_boss.h"
#include "k_color.h"
#include "k_respawn.h"
#include "k_bot.h"
@ -4250,6 +4251,9 @@ boolean P_SupermanLook4Players(mobj_t *actor)
if (players[c].mo->health <= 0)
continue; // dead
if ((gametyperules & GTR_BUMPERS) && players[c].bumpers <= 0)
continue; // other dead
playersinthegame[stop] = &players[c];
stop++;
}
@ -4970,6 +4974,9 @@ void P_RunOverlays(void)
mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP) | (mo->target->eflags & MFE_VERTICALFLIP);
mo->scale = mo->destscale = mo->target->scale;
mo->angle = mo->target->angle + mo->movedir;
mo->rollangle = mo->target->rollangle;
mo->pitch = mo->target->pitch;
mo->roll = mo->target->roll;
if ((mo->flags & MF_DONTENCOREMAP) != (mo->target->flags & MF_DONTENCOREMAP))
mo->flags ^= MF_DONTENCOREMAP;
@ -5360,6 +5367,8 @@ static void P_MobjSceneryThink(mobj_t *mobj)
}
else
P_AddOverlay(mobj);
if (mobj->target->hitlag) // move to the correct position, update to the correct properties, but DON'T STATE-ANIMATE
return;
break;
case MT_WATERDROP:
P_SceneryCheckWater(mobj);
@ -7850,7 +7859,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
mobj->extravalue1 = 1;
}
if (!S_SoundPlaying(mobj, mobj->info->attacksound))
if (mobj->extravalue1 != 2 && !S_SoundPlaying(mobj, mobj->info->attacksound))
S_StartSound(mobj, mobj->info->attacksound);
if (mobj->extravalue2 <= 8) // Short delay
@ -8717,6 +8726,32 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
}
break;
case MT_RANDOMITEM:
if ((leveltime == starttime) && !(gametyperules & GTR_CIRCUIT) && (mobj->flags2 & MF2_BOSSNOTRAP)) // here on map start?
{
if (gametyperules & GTR_PAPERITEMS)
{
if (battlecapsules == true || bossinfo.boss == true)
{
;
}
else
{
mobj_t *paperspawner = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PAPERITEMSPOT);
paperspawner->spawnpoint = mobj->spawnpoint;
mobj->spawnpoint->mobj = paperspawner;
P_RemoveMobj(mobj);
return false;
}
}
// poof into existance
P_UnsetThingPosition(mobj);
mobj->flags &= ~(MF_NOCLIPTHING|MF_NOBLOCKMAP);
mobj->renderflags &= ~RF_DONTDRAW;
P_SetThingPosition(mobj);
P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_EXPLODE);
nummapboxes++;
}
// FALLTHRU
case MT_SPHEREBOX:
if (gametype == GT_BATTLE && mobj->threshold == 70)
{
@ -8851,7 +8886,7 @@ static boolean P_FuseThink(mobj_t *mobj)
if (mobj->type == MT_SNAPPER_HEAD || mobj->type == MT_SNAPPER_LEG || mobj->type == MT_MINECARTSEG)
mobj->renderflags ^= RF_DONTDRAW;
if (mobj->fuse <= TICRATE && ((mobj->type == MT_RANDOMITEM && mobj->threshold == 69) || mobj->type == MT_EGGMANITEM || mobj->type == MT_FALLINGROCK))
if (mobj->fuse <= TICRATE && (mobj->type == MT_RANDOMITEM || mobj->type == MT_EGGMANITEM || mobj->type == MT_FALLINGROCK))
mobj->renderflags ^= RF_DONTDRAW;
mobj->fuse--;
@ -8903,10 +8938,13 @@ static boolean P_FuseThink(mobj_t *mobj)
}
return false;
case MT_RANDOMITEM:
if ((gametyperules & GTR_BUMPERS) && (mobj->threshold != 70))
if (mobj->flags2 & MF2_DONTRESPAWN)
{
if (mobj->threshold != 69)
break;
;
}
else if ((gametyperules & GTR_BUMPERS) && (mobj->threshold != 70))
{
break;
}
else
{
@ -8921,8 +8959,7 @@ static boolean P_FuseThink(mobj_t *mobj)
else
newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type);
P_SpawnMobj(newmobj->x, newmobj->y, newmobj->z, MT_EXPLODE); // poof into existance
// Transfer flags2 (strongbox, objectflip)
// Transfer flags2 (strongbox, objectflip, bossnotrap)
newmobj->flags2 = mobj->flags2;
if (mobj->threshold == 70)
newmobj->threshold = 70;
@ -10630,29 +10667,30 @@ void P_RespawnBattleBoxes(void)
box = (mobj_t *)th;
if (box->type != MT_RANDOMITEM || box->threshold != 68 || box->fuse) // only popped items
continue;
if (box->type != MT_RANDOMITEM
|| (box->flags2 & MF2_DONTRESPAWN)
|| box->threshold != 68
|| box->fuse
|| ((tic_t)box->cvmem+1 >= leveltime))
continue; // only popped items
// Respawn from mapthing if you have one!
if (box->spawnpoint)
{
P_SpawnMapThing(box->spawnpoint);
newmobj = box->spawnpoint->mobj; // this is set to the new mobj in P_SpawnMapThing
P_SpawnMobj(box->spawnpoint->mobj->x, box->spawnpoint->mobj->y, box->spawnpoint->mobj->z, MT_EXPLODE); // poof into existance
}
else
{
newmobj = P_SpawnMobj(box->x, box->y, box->z, box->type);
P_SpawnMobj(newmobj->x, newmobj->y, newmobj->z, MT_EXPLODE); // poof into existance
}
// Transfer flags2 (strongbox, objectflip)
// Transfer flags2 (strongbox, objectflip, bossnotrap)
newmobj->flags2 = box->flags2;
P_RemoveMobj(box); // make sure they disappear
numgotboxes--; // you've restored a box, remove it from the count
if (numgotboxes < 0)
numgotboxes = 0;
if (numgotboxes > 0)
numgotboxes--; // you've restored a box, remove it from the count
}
}
@ -10678,8 +10716,8 @@ void P_RespawnSpecials(void)
INT32 time = 30*TICRATE; // Respawn things in empty dedicated servers
mapthing_t *mthing = NULL;
//if (!(gametyperules & GTR_CIRCUIT) && numgotboxes >= (4*nummapboxes/5)) // Battle Mode respawns all boxes in a different way
//P_RespawnBattleBoxes();
if (!(gametyperules & GTR_CIRCUIT) && nummapboxes && (numgotboxes >= (4*nummapboxes/5))) // Battle Mode respawns all boxes in a different way
P_RespawnBattleBoxes();
// wait time depends on player count
for (p = 0; p < MAXPLAYERS; p++)
@ -10752,7 +10790,7 @@ void P_RespawnSpecials(void)
//
void P_SpawnPlayer(INT32 playernum)
{
UINT8 i, pcount = 0;
UINT8 i, pcount = 0; // MAXPLAYERS if exiting
player_t *p = &players[playernum];
mobj_t *mobj;
@ -10765,6 +10803,11 @@ void P_SpawnPlayer(INT32 playernum)
continue;
if (!playeringame[i] || players[i].spectator)
continue;
if (players[i].exiting)
{
pcount = MAXPLAYERS;
break;
}
if (players[i].jointime <= 1) // Prevent splitscreen hosters/joiners from only adding 1 player at a time in empty servers
continue;
pcount++;
@ -10899,29 +10942,14 @@ void P_SpawnPlayer(INT32 playernum)
{
if ((leveltime < starttime) || (pcount <= 1)) // Start of the map?
{
// Reset those bumpers!
p->bumpers = K_StartingBumperCount();
}
if (p->bumpers)
{
angle_t diff = FixedAngle(360*FRACUNIT/p->bumpers);
angle_t newangle = mobj->angle;
fixed_t newx = mobj->x + P_ReturnThrustX(mobj, newangle + ANGLE_180, 64*FRACUNIT);
fixed_t newy = mobj->y + P_ReturnThrustY(mobj, newangle + ANGLE_180, 64*FRACUNIT);
mobj_t *mo;
for (i = 0; i < p->bumpers; i++)
if (leveltime > 2) // Reset those bumpers!
{
mo = P_SpawnMobj(newx, newy, mobj->z, MT_BATTLEBUMPER);
mo->threshold = i;
P_SetTarget(&mo->target, mobj);
mo->angle = (diff * (i-1));
mo->color = mobj->color;
if (mobj->renderflags & RF_DONTDRAW)
mo->renderflags |= RF_DONTDRAW;
else
mo->renderflags &= ~RF_DONTDRAW;
p->bumpers = K_StartingBumperCount();
K_SpawnPlayerBattleBumpers(p);
}
else // temp, will get overwritten in K_BattleInit
{
p->bumpers = 1;
}
}
}
@ -11271,6 +11299,11 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
break;
}
// No bosses outside of a combat situation.
// (just in case we want boss arenas to do double duty as battle maps)
if (!bossinfo.boss && (mobjinfo[i].flags & MF_BOSS))
return false;
if (metalrecording) // Metal Sonic can't use these things.
{
if (mobjinfo[i].flags & (MF_ENEMY|MF_BOSS))
@ -11284,10 +11317,10 @@ static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i)
{
(void)mthing;
if ((gametyperules & GTR_SPHERES) && (i == MT_RING))
if ((i == MT_RING) && (gametyperules & GTR_SPHERES))
return MT_BLUESPHERE;
if ((gametyperules & GTR_PAPERITEMS) && (i == MT_RANDOMITEM))
if ((i == MT_RANDOMITEM) && (gametyperules & (GTR_PAPERITEMS|GTR_CIRCUIT)) == (GTR_PAPERITEMS|GTR_CIRCUIT) && !bossinfo.boss)
return MT_PAPERITEMSPOT;
return i;
@ -12215,6 +12248,26 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
}
break;
}
case MT_RANDOMITEM:
{
boolean delayed = !(gametyperules & GTR_CIRCUIT);
if (leveltime < (delayed ? starttime : 3))
{
mobj->flags2 |= MF2_BOSSNOTRAP; // mark as here on map start
if (delayed)
{
P_UnsetThingPosition(mobj);
mobj->flags |= (MF_NOCLIPTHING|MF_NOBLOCKMAP);
mobj->renderflags |= RF_DONTDRAW;
P_SetThingPosition(mobj);
}
}
else
{
P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_EXPLODE);
}
break;
}
case MT_ITEMCAPSULE:
{
// we have to adjust for reverse gravity early so that the below grounded checks work

View file

@ -332,6 +332,7 @@ static void P_NetArchivePlayers(void)
WRITEINT16(save_p, players[i].karmadelay);
WRITEUINT32(save_p, players[i].overtimekarma);
WRITEINT16(save_p, players[i].spheres);
WRITEINT16(save_p, players[i].spheredigestion);
WRITESINT8(save_p, players[i].glanceDir);
WRITEUINT8(save_p, players[i].tripWireState);
@ -597,6 +598,7 @@ static void P_NetUnArchivePlayers(void)
players[i].karmadelay = READINT16(save_p);
players[i].overtimekarma = READUINT32(save_p);
players[i].spheres = READINT16(save_p);
players[i].spheredigestion = READINT16(save_p);
players[i].glanceDir = READSINT8(save_p);
players[i].tripWireState = READUINT8(save_p);

View file

@ -86,11 +86,12 @@
// SRB2Kart
#include "k_kart.h"
#include "k_race.h"
#include "k_battle.h" // K_SpawnBattleCapsules
#include "k_battle.h" // K_BattleInit
#include "k_pwrlv.h"
#include "k_waypoint.h"
#include "k_bot.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_terrain.h" // TRF_TRIPWIRE
#include "k_brightmap.h"
#include "k_director.h" // K_InitDirector
@ -3524,7 +3525,7 @@ static void P_InitLevelSettings(void)
if (playeringame[i] && !players[i].spectator)
p++;
if (grandprixinfo.gp == false)
if (grandprixinfo.gp == false && bossinfo.boss == false)
players[i].lives = 3;
G_PlayerReborn(i, true);
@ -3549,6 +3550,12 @@ static void P_InitLevelSettings(void)
franticitems = false;
comeback = true;
}
else if (bossinfo.boss)
{
gamespeed = KARTSPEED_EASY;
franticitems = false;
comeback = true;
}
else if (modeattacking)
{
// Just play it safe and set everything
@ -4073,6 +4080,15 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
}
F_RunWipe(wipedefs[wipe_level_toblack], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false);
{
sfxenum_t kstart = sfx_kstart;
if (bossinfo.boss)
kstart = sfx_ssa021;
else if (encoremode)
kstart = sfx_ruby2;
S_StartSound(NULL, kstart);
}
}
/*if (!titlemapinaction)
wipegamestate = GS_LEVEL;*/
@ -4280,6 +4296,21 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
K_UpdateMatchRaceBots();
}
if (bossinfo.boss)
{
// Reset some pesky boss state that can't be handled elsewhere.
bossinfo.barlen = BOSSHEALTHBARLEN;
bossinfo.visualbar = 0;
if (bossinfo.enemyname)
Z_Free(bossinfo.enemyname);
if (bossinfo.subtitle)
Z_Free(bossinfo.subtitle);
bossinfo.enemyname = bossinfo.subtitle = NULL;
bossinfo.titleshow = 0;
bossinfo.titlesound = sfx_typri1;
memset(&(bossinfo.weakspots), 0, sizeof(weakspot_t)*NUMWEAKSPOTS);
}
if (!fromnetsave) // uglier hack
{ // to make a newly loaded level start on the second frame.
INT32 buf = gametic % BACKUPTICS;

View file

@ -33,6 +33,7 @@
#include "k_kart.h"
#include "k_race.h"
#include "k_battle.h"
#include "k_boss.h"
#include "k_waypoint.h"
#include "k_director.h"
@ -327,6 +328,7 @@ if ((*mop = targ) != NULL) // Set new target and if non-NULL, increase its count
static inline void P_RunThinkers(void)
{
size_t i;
for (i = 0; i < NUM_THINKERLISTS; i++)
{
ps_thlist_times[i] = I_GetPreciseTime();
@ -601,24 +603,36 @@ void P_Ticker(boolean run)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
P_PlayerAfterThink(&players[i]);
// Plays the music after the starting countdown.
if (leveltime == (starttime + (TICRATE/2)))
// Bosses have a punchy start, so no position.
if (bossinfo.boss == true)
{
S_ChangeMusic(mapmusname, mapmusflags, true);
S_ShowMusicCredit();
}
if (encoremode)
{
// Encore humming starts immediately.
if (leveltime == 3)
S_ChangeMusicInternal("encore", true);
{
S_ChangeMusic(mapmusname, mapmusflags, true);
S_ShowMusicCredit();
}
}
// Plays the music after the starting countdown.
else
{
// Plays the POSITION music after the camera spin
if (leveltime == introtime)
S_ChangeMusicInternal("postn", true);
if (leveltime == (starttime + (TICRATE/2)))
{
S_ChangeMusic(mapmusname, mapmusflags, true);
S_ShowMusicCredit();
}
if (encoremode)
{
// Encore humming starts immediately.
if (leveltime == 3)
S_ChangeMusicInternal("encore", true);
}
else
{
// Plays the POSITION music after the camera spin
if (leveltime == introtime)
S_ChangeMusicInternal("postn", true);
}
}
ps_lua_thinkframe_time = I_GetPreciseTime();
@ -656,6 +670,8 @@ void P_Ticker(boolean run)
if (hyubgone > 0)
hyubgone--;
K_BossInfoTicker();
if ((gametyperules & GTR_BUMPERS))
{
if (wantedcalcdelay && --wantedcalcdelay <= 0)

View file

@ -52,6 +52,7 @@
#include "k_respawn.h"
#include "k_bot.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_terrain.h" // K_SpawnSplashForMobj
#include "k_color.h"
@ -707,7 +708,7 @@ boolean P_EndingMusic(player_t *player)
{
char buffer[9];
boolean looping = true;
INT32 bestlocalpos;
INT32 bestlocalpos, test;
player_t *bestlocalplayer;
if (!P_IsLocalPlayer(player)) // Only applies to a local player
@ -718,27 +719,35 @@ boolean P_EndingMusic(player_t *player)
// Event - Level Finish
// Check for if this is valid or not
#define getplayerpos(p) \
(((players[p].position < 1) || (players[p].pflags & PF_NOCONTEST)) \
? MAXPLAYERS+1 \
: players[p].position);
if (r_splitscreen)
{
if (!((players[displayplayers[0]].exiting || (players[displayplayers[0]].pflags & PF_NOCONTEST))
|| (players[displayplayers[1]].exiting || (players[displayplayers[1]].pflags & PF_NOCONTEST))
|| ((r_splitscreen < 2) && (players[displayplayers[2]].exiting || (players[displayplayers[2]].pflags & PF_NOCONTEST)))
|| ((r_splitscreen < 3) && (players[displayplayers[3]].exiting || (players[displayplayers[3]].pflags & PF_NOCONTEST)))))
INT32 *localplayertable = (splitscreen_partied[consoleplayer] ? splitscreen_party[consoleplayer] : g_localplayers);
if (!((players[localplayertable[0]].exiting || (players[localplayertable[0]].pflags & PF_NOCONTEST))
|| (players[localplayertable[1]].exiting || (players[localplayertable[1]].pflags & PF_NOCONTEST))
|| ((r_splitscreen < 2) && (players[localplayertable[2]].exiting || (players[localplayertable[2]].pflags & PF_NOCONTEST)))
|| ((r_splitscreen < 3) && (players[localplayertable[3]].exiting || (players[localplayertable[3]].pflags & PF_NOCONTEST)))))
return false;
bestlocalplayer = &players[displayplayers[0]];
bestlocalpos = ((players[displayplayers[0]].pflags & PF_NOCONTEST) ? MAXPLAYERS+1 : players[displayplayers[0]].position);
bestlocalplayer = &players[localplayertable[0]];
bestlocalpos = getplayerpos(localplayertable[0]);
#define setbests(p) \
if (((players[p].pflags & PF_NOCONTEST) ? MAXPLAYERS+1 : players[p].position) < bestlocalpos) \
test = getplayerpos(p); \
if (test < bestlocalpos) \
{ \
bestlocalplayer = &players[p]; \
bestlocalpos = ((players[p].pflags & PF_NOCONTEST) ? MAXPLAYERS+1 : players[p].position); \
bestlocalpos = test; \
}
setbests(displayplayers[1]);
setbests(localplayertable[1]);
if (r_splitscreen > 1)
setbests(displayplayers[2]);
setbests(localplayertable[2]);
if (r_splitscreen > 2)
setbests(displayplayers[3]);
setbests(localplayertable[3]);
#undef setbests
}
else
@ -747,9 +756,11 @@ boolean P_EndingMusic(player_t *player)
return false;
bestlocalplayer = player;
bestlocalpos = ((player->pflags & PF_NOCONTEST) ? MAXPLAYERS+1 : player->position);
bestlocalpos = getplayerpos((player-players));
}
#undef getplayerpos
if ((gametyperules & GTR_CIRCUIT) && bestlocalpos == MAXPLAYERS+1)
sprintf(buffer, "k*fail"); // F-Zero death results theme
else
@ -805,7 +816,9 @@ void P_RestoreMusic(player_t *player)
return;
// Event - Level Start
if (leveltime >= (starttime + (TICRATE/2))) // see also where time overs are handled - search for "lives = 2" in this file
if (bossinfo.boss == false && (leveltime < (starttime + (TICRATE/2)))) // see also where time overs are handled
return;
{
INT32 wantedmus = 0; // 0 is level music, 1 is invincibility, 2 is grow
@ -3029,7 +3042,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
else
timeover = 0;
if (!(player->playerstate == PST_DEAD || player->exiting))
if (!(player->playerstate == PST_DEAD || player->exiting || leveltime < introtime))
{
if (player->spectator) // force cam off for spectators
return true;
@ -3776,7 +3789,7 @@ void P_DoTimeOver(player_t *player)
legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p
}
if (netgame && !player->bot)
if (netgame && !player->bot && !bossinfo.boss)
{
CON_LogMessage(va(M_GetText("%s ran out of time.\n"), player_names[player-players]));
}
@ -4347,16 +4360,36 @@ void P_PlayerThink(player_t *player)
// Accessibility - kickstart your acceleration
if (!(player->pflags & PF_KICKSTARTACCEL))
{
player->kickstartaccel = 0;
}
else if (cmd->buttons & BT_ACCELERATE)
{
if (!player->exiting && !(player->pflags & PF_ACCELDOWN))
{
player->kickstartaccel = 0;
}
else if (player->kickstartaccel < ACCEL_KICKSTART)
{
player->kickstartaccel++;
if ((player->kickstartaccel == ACCEL_KICKSTART) && P_IsLocalPlayer(player))
{
S_StartSound(NULL, sfx_ding);
}
}
else // for HUD
{
player->kickstartaccel = ACCEL_KICKSTART+1;
}
}
else if (player->kickstartaccel < ACCEL_KICKSTART)
{
player->kickstartaccel = 0;
}
else // for HUD
{
player->kickstartaccel = ACCEL_KICKSTART+1;
}
#ifdef PARANOIA
if (player->playerstate == PST_REBORN)

View file

@ -762,16 +762,16 @@ boolean R_SpriteIsFlashing(vissprite_t *vis)
return (!(vis->cut & SC_PRECIP)
&& (vis->mobj->flags & (MF_ENEMY|MF_BOSS))
&& (vis->mobj->flags2 & MF2_FRET)
&& !(vis->mobj->flags & MF_GRENADEBOUNCE)
&& (leveltime & 1));
&& !(vis->mobj->flags & MF_GRENADEBOUNCE));
}
UINT8 *R_GetSpriteTranslation(vissprite_t *vis)
{
if (vis->mobj->hitlag > 0 && (vis->mobj->eflags & MFE_DAMAGEHITLAG))
if ((vis->mobj->hitlag > 0 && (vis->mobj->eflags & MFE_DAMAGEHITLAG)) || R_SpriteIsFlashing(vis))
{
return R_GetTranslationColormap(TC_HITLAG, 0, GTC_CACHE);
}
/*
else if (R_SpriteIsFlashing(vis)) // Bosses "flash"
{
if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized)
@ -781,6 +781,7 @@ UINT8 *R_GetSpriteTranslation(vissprite_t *vis)
else
return R_GetTranslationColormap(TC_BOSS, 0, GTC_CACHE);
}
*/
else if (vis->mobj->color)
{
// New colormap stuff for skins Tails 06-07-2002

View file

@ -30,6 +30,7 @@
#include "m_misc.h" // for tunes command
#include "m_cond.h" // for conditionsets
#include "lua_hook.h" // MusicChange hook
#include "k_boss.h" // bossinfo
#ifdef HW3SOUND
// 3D Sound Interface
@ -2351,7 +2352,9 @@ void S_StartEx(boolean reset)
S_StopMusic(); // Starting ambience should always be restarted, if playing.
if (leveltime < (starttime + (TICRATE/2))) // SRB2Kart
S_StartSound(NULL, encoremode ? sfx_ruby2 : sfx_kstart);
{
;
}
else
S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);

View file

@ -1100,6 +1100,8 @@ sfxinfo_t S_sfx[NUMSFX] =
{"cock", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Hammer cocks, bang bang
{"itcaps", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, "Item capsule"},
{"kstart", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Sonic Adventure shwing!
{"typri1", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SA2 boss typewriting 1
{"typri2", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SA2 final boss-type typewriting
// SRB2Kart - Engine sounds
// Engine class A

View file

@ -1164,6 +1164,8 @@ typedef enum
sfx_cock,
sfx_itcaps,
sfx_kstart,
sfx_typri1,
sfx_typri2,
// Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy...
// Engine class A - Low Speed, Low Weight

View file

@ -32,6 +32,7 @@
#include "m_anigif.h" // cv_gif_downscale
#include "p_setup.h" // NiGHTS grading
#include "k_grandprix.h" // we need to know grandprix status for titlecards
#include "k_boss.h"
//random index
#include "m_random.h"
@ -647,6 +648,9 @@ static patch_t *tcroundnum[10];
static patch_t *tcactnum[10];
static patch_t *tcact;
static patch_t *twarn;
static patch_t *twarn2;
// some coordinates define to make my life easier....
#define FINAL_ROUNDX (24)
#define FINAL_EGGY (160)
@ -700,6 +704,9 @@ static void ST_cacheLevelTitle(void)
tcact = (patch_t *)W_CachePatchName("TT_ACT", PU_HUDGFX);
twarn = (patch_t *)W_CachePatchName("K_BOSW01", PU_HUDGFX);
twarn2 = (patch_t *)W_CachePatchName("K_BOSW02", PU_HUDGFX);
// Cache round #
for (i=1; i < 11; i++)
{
@ -786,7 +793,36 @@ void ST_runTitleCard(void)
// SRB2KART
// side Zig-Zag positions...
if (bossinfo.boss == true)
{
// Handle name info...
if (bossinfo.enemyname)
{
UINT32 len = strlen(bossinfo.enemyname)+1;
if (len > 1 && bossinfo.titleshow < len)
{
len = (lt_endtime-(TICRATE/2))/len;
if (lt_ticker % len == 0)
{
char c = toupper(bossinfo.enemyname[bossinfo.titleshow]);
bossinfo.titleshow++;
c -= LT_FONTSTART;
if (c < 0 || c >= LT_FONTSIZE || !tc_font[1][(INT32)c] || !bossinfo.titlesound)
{
;
}
else
{
S_StartSound(NULL, bossinfo.titlesound);
}
}
}
}
// No matter the circumstances, scroll the WARN...
bannerx = -((lt_ticker*2)%((encoremode ? twarn2 : twarn)->width));
}
else
{
// TITLECARD START
if (lt_ticker < TTANIMSTART)
{
@ -908,7 +944,8 @@ void ST_runTitleCard(void)
}
// No matter the circumstances, scroll the banner...
bannerx = -(lt_ticker%(tcbanner->width));
bannerx = -((lt_ticker*2)%(tcbanner->width));
}
// used for hud slidein
@ -932,10 +969,9 @@ void ST_drawTitleCard(void)
fixed_t actscale;
angle_t fakeangle;
INT32 pad = ((vid.width/vid.dupx) - BASEVIDWIDTH)/2;
INT32 bx = bannerx; // We need to make a copy of that otherwise pausing will cause problems.
UINT8 i;
if (!G_IsTitleCardAvailable())
return;
@ -952,6 +988,103 @@ void ST_drawTitleCard(void)
if (lt_ticker < TTANIMSTART)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
if (bossinfo.boss == true)
{
// WARNING!
// https://twitter.com/matthewseiji/status/1485003284196716544
// the above tweet is directly responsible for the existence of bosses in this game at all
{
#define LOTIME 5
#define HITIME 15
patch_t *localwarn = (encoremode ? twarn2 : twarn);
INT32 transp = (lt_ticker+HITIME) % (LOTIME+HITIME);
boolean encorehack = (encoremode && lt_ticker <= PRELEVELTIME+4);
if ((localwarn->width > 0) && (lt_ticker + (HITIME-transp) <= lt_endtime))
{
if (transp > HITIME-1)
{
transp = HITIME-1;
}
transp = (((10*transp)/HITIME)<<V_ALPHASHIFT) | (encorehack ? V_SUBTRACT : V_ADD);
while (bx > -pad)
bx -= localwarn->width;
while (bx < BASEVIDWIDTH+pad)
{
V_DrawFixedPatch(bx*FRACUNIT, 55*FRACUNIT, FRACUNIT, V_SNAPTOLEFT|transp, localwarn, NULL);
bx += localwarn->width;
}
}
#undef LOTIME
#undef HITIME
}
// Everything else...
if (bossinfo.enemyname)
{
bx = V_TitleCardStringWidth(bossinfo.enemyname);
// Name.
V_DrawTitleCardString((BASEVIDWIDTH - bx)/2, 75, bossinfo.enemyname, 0, true, bossinfo.titleshow, lt_exitticker);
// Under-bar.
{
angle_t fakeang = 0;
fixed_t scalex = FRACUNIT;
// Handle scaling.
if (lt_ticker <= 3)
{
fakeang = (lt_ticker*ANGLE_45)/2;
scalex = FINESINE(fakeang>>ANGLETOFINESHIFT);
}
else if (lt_exitticker > 1)
{
if (lt_exitticker <= 4)
{
fakeang = ((lt_exitticker-1)*ANGLE_45)/2;
scalex = FINECOSINE(fakeang>>ANGLETOFINESHIFT);
}
else
{
scalex = 0;
}
}
// Handle subtitle.
else if (bossinfo.subtitle && lt_ticker >= TICRATE/2)
{
INT32 by = 75+32;
if (lt_ticker == TICRATE/2 || lt_exitticker == 1)
{
;
}
else if (lt_ticker == (TICRATE/2)+1 || lt_ticker == lt_endtime)
{
by += 3;
}
else
{
by += 5;
}
V_DrawRightAlignedThinString((BASEVIDWIDTH+bx)/2, by, V_6WIDTHSPACE, bossinfo.subtitle);
}
// Now draw the under-bar itself.
if (scalex > 0)
{
bx = FixedMul(bx, scalex);
V_DrawFill((BASEVIDWIDTH-(bx+2))/2, 75+32, bx+2, 3, 31);
V_DrawFill((BASEVIDWIDTH-(bx))/2, 75+32+1, bx, 1, 0);
}
}
}
lt_lasttic = lt_ticker;
goto luahook;
}
// Background zig-zags
V_DrawFixedPatch((chev1x)*FRACUNIT, (chev1y)*FRACUNIT, FRACUNIT, chevtflag, tcchev1, NULL);
V_DrawFixedPatch((chev2x)*FRACUNIT, (chev2y)*FRACUNIT, FRACUNIT, chevtflag, tcchev2, NULL);
@ -966,11 +1099,16 @@ void ST_drawTitleCard(void)
// round num background
V_DrawFixedPatch(roundnumx*FRACUNIT, roundnumy*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tccirclebg, NULL);
// Scrolling banner, we'll draw 3 of those back to back.
for (i=0; i < 3; i++)
// Scrolling banner
if (tcbanner->width > 0)
{
V_DrawFixedPatch((bannerx + bx)*FRACUNIT, (bannery)*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tcbanner, NULL);
bx += tcbanner->width;
while (bx > -pad)
bx -= tcbanner->width;
while (bx < BASEVIDWIDTH+pad)
{
V_DrawFixedPatch(bx*FRACUNIT, (bannery)*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tcbanner, NULL);
bx += tcbanner->width;
}
}
// If possible, draw round number
@ -982,10 +1120,10 @@ void ST_drawTitleCard(void)
V_DrawFixedPatch(eggx2*FRACUNIT, eggy2*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tccirclebottom, NULL);
// Now the level name.
V_DrawTitleCardString((actnum) ? 265 : 280, 60, lvlttl, V_SNAPTORIGHT, true, lt_ticker, TTANIMENDTHRESHOLD);
V_DrawTitleCardString((actnum) ? 265 : 280, 60, lvlttl, V_SNAPTORIGHT, false, lt_ticker, TTANIMENDTHRESHOLD);
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
V_DrawTitleCardString((actnum) ? 265 : 280, 60+32, strlen(zonttl) ? zonttl : "ZONE", V_SNAPTORIGHT, true, lt_ticker - strlen(lvlttl), TTANIMENDTHRESHOLD);
V_DrawTitleCardString((actnum) ? 265 : 280, 60+32, strlen(zonttl) ? zonttl : "ZONE", V_SNAPTORIGHT, false, lt_ticker - strlen(lvlttl), TTANIMENDTHRESHOLD);
// the act has a similar graphic animation, but we'll handle it here since it's only like 2 graphics lmfao.
if (actnum && actnum < 10)

View file

@ -1719,7 +1719,7 @@ INT32 V_TitleCardStringWidth(const char *str)
// V_DrawTitleCardScreen.
// see v_video.h's prototype for more information.
//
void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boolean alignright, INT32 timer, INT32 threshold)
void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boolean bossmode, INT32 timer, INT32 threshold)
{
INT32 xoffs = 0;
@ -1740,7 +1740,7 @@ void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boole
x -= 2; // Account for patch width...
if (alignright)
if (flags & V_SNAPTORIGHT)
x -= V_TitleCardStringWidth(str);
@ -1778,11 +1778,25 @@ void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boole
ol = tc_font[0][(INT32)c];
pp = tc_font[1][(INT32)c];
if (timer)
if (bossmode)
{
if (let_time <= 0)
return;
if (threshold > 0)
{
if (threshold > 3)
return;
fakeang = (threshold*ANGLE_45)/2;
scalex = FINECOSINE(fakeang>>ANGLETOFINESHIFT);
}
offs = ((FRACUNIT-scalex)*pp->width)/2;
}
else if (timer)
{
// make letters appear
if (!threshold || let_time < threshold)
if (!threshold)
;
else if (let_time < threshold)
{
if (let_time <= 0)
return; // No reason to continue drawing, none of the next letters will be drawn either.
@ -1792,7 +1806,7 @@ void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boole
fakeang = min(360 + 90, let_time*41) * ANG1;
scalex = FINESINE(fakeang>>ANGLETOFINESHIFT);
}
else if (let_time > threshold)
else if (!bossmode && let_time > threshold)
{
// Make letters disappear...
let_time -= threshold;

View file

@ -269,7 +269,7 @@ void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, con
// threshold: when the letters start disappearing (leave to 0 to disable) (both are INT32 in case you supply negative values...)
// NOTE: This function ignores most conventional string flags (V_RETURN8, V_ALLOWLOWERCASE ...)
// NOTE: This font only works with uppercase letters.
void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boolean alignright, INT32 timer, INT32 threshold);
void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boolean bossmode, INT32 timer, INT32 threshold);
// returns thr width of a string drawn using the above function.
INT32 V_TitleCardStringWidth(const char *str);

View file

@ -42,6 +42,7 @@
#include "m_random.h" // M_RandomKey
#include "g_input.h" // PlayerInputDown
#include "k_battle.h"
#include "k_boss.h"
#include "k_pwrlv.h"
#include "console.h" // cons_menuhighlight
#include "k_grandprix.h"
@ -222,7 +223,14 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
else
{
// set up the levelstring
if (mapheaderinfo[prevmap]->levelflags & LF_NOZONE)
if (bossinfo.boss == true && bossinfo.enemyname)
{
snprintf(data.levelstring,
sizeof data.levelstring,
"* %s *",
bossinfo.enemyname);
}
else if (mapheaderinfo[prevmap]->levelflags & LF_NOZONE)
{
if (mapheaderinfo[prevmap]->actnum > 0)
snprintf(data.levelstring,
@ -481,7 +489,7 @@ void Y_IntermissionDrawer(void)
else
hilicol = ((intertype == int_race) ? V_SKYMAP : V_REDMAP);
if (sorttic != -1 && intertic > sorttic && multiplayer)
if (sorttic != -1 && intertic > sorttic)
{
INT32 count = (intertic - sorttic);
@ -493,7 +501,7 @@ void Y_IntermissionDrawer(void)
x += (((16 - count) * vid.width) / (8 * vid.dupx));
}
if (intertype == int_race || intertype == int_battle)
if (intertype == int_race || intertype == int_battle || intertype == int_battletime)
{
#define NUMFORNEWCOLUMN 8
INT32 y = 41, gutter = ((data.numplayers > NUMFORNEWCOLUMN) ? 0 : (BASEVIDWIDTH/2));
@ -516,19 +524,11 @@ void Y_IntermissionDrawer(void)
{
switch (intertype)
{
default:
case int_race:
timeheader = "TIME";
break;
case int_battle:
if (battlecapsules)
{
timeheader = "TIME";
}
else
{
timeheader = "SCORE";
}
timeheader = "SCORE";
break;
default:
timeheader = "TIME";
break;
}
}
@ -653,7 +653,7 @@ void Y_IntermissionDrawer(void)
V_DrawRightAlignedThinString(x+152+gutter, y-1, (data.numplayers > NUMFORNEWCOLUMN ? V_6WIDTHSPACE : 0), "NO CONTEST.");
else
{
if (intertype == int_race || (intertype == int_battle && battlecapsules))
if (intertype == int_race || intertype == int_battletime)
{
snprintf(strtime, sizeof strtime, "%i'%02i\"%02i", G_TicsToMinutes(data.val[i], true),
G_TicsToSeconds(data.val[i]), G_TicsToCentiseconds(data.val[i]));
@ -695,7 +695,7 @@ skiptallydrawer:
if (!LUA_HudEnabled(hud_intermissionmessages))
return;
if (timer && grandprixinfo.gp == false)
if (timer && grandprixinfo.gp == false && bossinfo.boss == false)
{
char *string;
INT32 tickdown = (timer+1)/TICRATE;
@ -797,11 +797,11 @@ void Y_Ticker(void)
if (intertic < TICRATE || intertic & 1 || endtic != -1)
return;
if (intertype == int_race || intertype == int_battle)
if (intertype == int_race || intertype == int_battle || intertype == int_battletime)
{
//if (!(multiplayer && demo.playback)) // Don't advance to rankings in replays
{
if (!data.rankingsmode && (intertic >= sorttic + 8))
if (!data.rankingsmode && sorttic != -1 && (intertic >= sorttic + 8))
{
Y_CalculateMatchData(1, Y_CompareRank);
}
@ -1034,12 +1034,19 @@ void Y_DetermineIntermissionType(void)
if (intermissiontypes[gametype] != int_none)
intertype = intermissiontypes[gametype];
else if (modeattacking)
intertype = int_timeattack;
else if (gametype == GT_RACE)
intertype = int_race;
else if (gametype == GT_BATTLE)
intertype = int_battle;
{
UINT8 i = 0, nump = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
nump++;
}
intertype = (nump < 2 ? int_battletime : int_battle);
}
}
//
@ -1049,6 +1056,14 @@ void Y_DetermineIntermissionType(void)
//
void Y_StartIntermission(void)
{
UINT8 i = 0, nump = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
nump++;
}
intertic = -1;
#ifdef PARANOIA
@ -1059,30 +1074,29 @@ void Y_StartIntermission(void)
// set player Power Level type
powertype = K_UsingPowerLevels();
if (!multiplayer)
// determine the tic the intermission ends
if (!multiplayer || demo.playback)
{
timer = 20*TICRATE;
timer = ((nump >= 2) ? 10 : 5)*TICRATE;
}
else
{
if (cv_inttime.value == 0)
timer = 1;
else if (demo.playback && !multiplayer) // Override inttime (which is pulled from the replay anyway
timer = 10*TICRATE;
else
{
timer = cv_inttime.value*TICRATE;
timer = cv_inttime.value*TICRATE;
if (!timer)
timer = 1;
}
if (!timer)
timer = 1; // prevent a weird bug
}
// determine the tic everybody's scores/PWR starts getting sorted
sorttic = -1;
if (multiplayer || nump >= 2)
{
sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE); // 8 second pause after match results
}
if (intermissiontypes[gametype] != int_none)
intertype = intermissiontypes[gametype];
sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE); // 8 second pause after match results
// We couldn't display the intermission even if we wanted to.
// But we still need to give the players their score bonuses, dummy.
//if (dedicated) return;
@ -1094,23 +1108,20 @@ void Y_StartIntermission(void)
switch (intertype)
{
case int_battle:
case int_battletime:
{
// Calculate who won
if (battlecapsules)
{
Y_CalculateMatchData(0, Y_CompareTime);
}
else
{
Y_CalculateMatchData(0, Y_CompareScore);
}
if (cv_inttime.value > 0)
S_ChangeMusicInternal("racent", true); // loop it
break;
// Calculate who won
if (intertype == int_battle)
{
Y_CalculateMatchData(0, Y_CompareScore);
break;
}
}
case int_race: // (time-only race)
// FALLTHRU
case int_race:
{
// Calculate who won
Y_CalculateMatchData(0, Y_CompareTime);

View file

@ -32,8 +32,8 @@ typedef enum
{
int_none,
int_race, // Race
int_battle, // Battle
int_timeattack, // Time Attack
int_battle, // Battle (score-based)
int_battletime, // Battle (time-based)
} intertype_t;
extern intertype_t intertype;