Merge branch 'blankart-dev' into newinput

This commit is contained in:
GenericHeroGuy 2025-03-10 23:26:30 +01:00
commit e61c43a852
39 changed files with 1004 additions and 440 deletions

View file

@ -803,10 +803,6 @@ keywords
Returns the best position of all non-CPU players.\n
Intended for branching camera movement in podium maps.";
PodiumFinish = "void PodiumFinish(void)\n
Brings up final Grand Prix results screen in podium maps.\n
Does nothing in standard maps.";
SetLineRenderStyle = "void SetLineRenderStyle(int tag, int blend, fixed alpha)\n
Changes the rendering of the tagged linedefs' middle textures.\n
- tag: The linedef tag to change.\n
@ -849,18 +845,17 @@ keywords
Ends the level.
- showintermission: Whether to show the results screen. If omitted, use the default behavior.";
Music_Play = "void Music_Play(str tune, [bool onlyforactivator])\n
Play a tune. If it's already playing, restarting from the beginning.\n
- tune: The ID of the tune. Note: this is separate from the music lump.\n
- onlyforactivator: Only play the tune for the activator (if activator is a player).";
Music_Play = "void Music_Play(str song, [bool onlyforactivator])\n
Play a song. If it's already playing, restarting from the beginning.\n
- song: The name of the song lump, without 'O_' at the beginning and without a file extension.\n
- onlyforactivator: Only play the song for the activator (if activator is a player).";
Music_StopAll = "void Music_StopAll([bool onlyforactivator])\n
Stop every tune that is currently playing.\n
Stop the music that is currently playing.\n
- onlyforactivator: Only stop for the activator (if activator is a player).";
Music_Remap = "void Music_Remap(str tune, str song, [bool onlyforactivator])\n
Change the actual song lump that a tune will play.\n
- tune: The ID of the tune.\n
Music_Remap = "void Music_Remap(str song, [bool onlyforactivator])\n
Change the song thats playing while keeping its position.\n
- song: The name of the song lump, without 'O_' at the beginning and without a file extension.\n
- onlyforactivator: Only remap for the activator (if activator is a player).";
@ -869,61 +864,6 @@ keywords
- fade: Time (tics) to fade between full volume and silence.\n
- duration: Silent duration (tics) (not including fade in and fade out), -1 = infinite (default if omitted).";
Freeze = "void Freeze(bool value)\n
Pauses or unpauses the level's thinkers.\n
- value: True to freeze, false to unfreeze.";
Dialogue_SetSpeaker = "void Dialogue_SetSpeaker(str character, int sprite)\n
Display a new dialogue box, using a player skin.\n
- character: The name of the skin to use.\n
- sprite: Which frame of the TALK sprite to display.";
Dialogue_SetCustomSpeaker = "void Dialogue_SetCustomSpeaker(str nametag, str graphic, [str color, str voice])\n
Display a new dialogue box, using a custom nametag, graphic, and voice.\n
- nametag: The name to display on the dialogue box.\n
- graphic: The name of the graphic lump to display.\n
- color: The name of a skincolor to use for the graphic. Defaults to 'None'.\n
- voice: The name of the voice sound effect to use. Defaults to 'sfx_ktalk'.";
Dialogue_NewText = "void Dialogue_NewText(str text)\n
Set the text to start displaying on the current dialogue box.\n
- text: The contents of the dialogue box.";
Dialogue_WaitForDismiss = "void Dialogue_WaitForDismiss(void)\n
Pause the current script until the current dialogue box\n
has been dismissed by the player.";
Dialogue_WaitForText = "void Dialogue_WaitForText(void)\n
Pause the current script until the current dialogue box\n
finishes rendering all of its text.";
Dialogue_NewDismissText = "void Dialogue_NewDismissText(str text)\n
Sets new text to display on the dialogue box, and then waits for\n
the player to dismiss it. This is exactly equivalent to calling\n
Dialogue_NewText and then Dialogue_WaitForDismiss immediately after.\n
- text: The contents of the dialogue box.";
Dialogue_AutoDismiss = "void Dialogue_AutoDismiss(void)\n
Dismisses the current dialogue (including from other threads).";
AddMessage = "void AddMessage(str message, bool interrupt, bool persist)\n
Display a message at the top of every player's HUD.\n
- message: Text of the message.\n
- interrupt: True to interrupt other messages, False to display when they're done.\n
- persist: True to last forever (Tutorial objectives), False to disappear after a time limit.";
AddMessageForPlayer = "void AddMessageForPlayer(str message, bool interrupt, bool persist)\n
Display a message at the top of the triggering player's HUD.\n
- message: Text of the message.\n
- interrupt: True to interrupt other messages, False to display when they're done.\n
- persist: True to last forever (Tutorial objectives), False to disappear after a time limit.";
ClearPersistentMessages = "void ClearPersistentMessages(void)\n
Remove all HUD messages (AddMessage, AddMessageForPlayer) that are set to persist, for all players.\n";
ClearPersistentMessagesForPlayer = "void ClearPersistentMessagesForPlayer(void)\n
Remove the triggering player's HUD messages (AddMessage, AddMessageForPlayer) that are set to persist.\n";
FinishLine = "void FinishLine([bool flip])\n
Increments the current lap of the activating player, like when crossing the finish line.\n
If called from an activating line and from the wrong side, then it will decrement a lap instead.\n
@ -949,9 +889,6 @@ keywords
- BOT_CONTROLLER_FASTFALL: Bots will try to fastfall when they're in the air.\n
- forcedir: Force the bots to drive in an exact angle. Requires BOT_CONTROLLER_FORCEDIR to be set.";
DismountFlyingObject = "void DismountFlyingObject(void)\n
Makes the activator player dismount their Dead Line Rocket or Rideroid.";
GetLineProperty = "any GetLineProperty(int tag, int property)\n
Gets the value of a line property directly.\n
- tag: The line tag to retrieve the property from. 0 uses the activating line, if it exists.\n

View file

@ -298,7 +298,7 @@ special
void -500:CameraWait(1, int),
int -501:PodiumPosition(0),
void -502:PodiumFinish(0),
//void -502:PodiumFinish(0),
void -503:SetLineRenderStyle(3, int, int, fixed),
void -504:MapWarp(2, str, bool),
int -505:AddBot(0, str, int, int),

View file

@ -3189,7 +3189,7 @@ bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W
/*--------------------------------------------------
bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Play a tune. If it's already playing, restart from the
Play a song. If it's already playing, restart from the
beginning.
--------------------------------------------------*/
bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
@ -3204,7 +3204,7 @@ bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W
return false;
}
S_ChangeMusicEx(map->getString(argV[0])->str, 0, false, mapmusposition, 0, 0);
S_ChangeMusicEx(map->getString(argV[0])->str, 0, true, mapmusposition, 0, 0);
return false;
}
@ -3232,22 +3232,22 @@ bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM
/*--------------------------------------------------
bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
Change the actual song lump that a tune will play.
Change the song while keeping the same music posititon.
--------------------------------------------------*/
bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{
ACSVM::MapScope *map = thread->scopeMap;
UINT32 lastmapmusposition = mapmusposition;
// 0: str tune - id for the tune to play
// 1: str song - lump name for the song to map to
// 2: [bool foractivator] - only do this if the activator is a player and is being viewed
// 0: str song - lump name for the song to map to
// 1: [bool foractivator] - only do this if the activator is a player and is being viewed
if (argC > 2 && argV[2] && !ACS_ActivatorIsLocal(thread))
if (argC > 1 && argV[1] && !ACS_ActivatorIsLocal(thread))
{
return false;
}
S_ChangeMusicEx(map->getString(argV[1])->str, 0, false, mapmusposition, 0, 0);
S_ChangeMusicEx(map->getString(argV[0])->str, 0, true, lastmapmusposition, 0, 0);
return false;
}

View file

@ -144,15 +144,10 @@ void ACS_LoadLevelScripts(size_t mapID)
// Insert BEHAVIOR lump into the list.
{
static const char *maplumpname;
maplumpname = G_BuildMapName(gamemap);
ACSVM::ModuleName name = ACSVM::ModuleName(
env->getString( maplumpname ),
env->getString( mapheaderinfo[mapID]->lumpname ),
nullptr,
W_CheckNumForMap(maplumpname)
mapheaderinfo[mapID]->lumpnum
);
modules.push_back(env->getModule(name));
@ -275,11 +270,11 @@ void ACS_RunLapScript(mobj_t *mo, line_t *line)
}
/*--------------------------------------------------
void ACS_RunPositionScript(void)
void ACS_RunPlayerFinishScript(player_t *player)
See header file for description.
--------------------------------------------------*/
void ACS_RunPositionScript(void)
void ACS_RunPlayerFinishScript(player_t *player)
{
Environment *env = &ACSEnv;
@ -287,7 +282,30 @@ void ACS_RunPositionScript(void)
ACSVM::HubScope *const hub = global->getHubScope(0);
ACSVM::MapScope *const map = hub->getMapScope(0);
map->scriptStartType(ACS_ST_POSITION, {});
ACSVM::MapScope::ScriptStartInfo scriptInfo;
ThreadInfo info;
P_SetTarget(&info.mo, player->mo);
scriptInfo.info = &info;
map->scriptStartTypeForced(ACS_ST_FINISH, scriptInfo);
}
/*--------------------------------------------------
void ACS_RunRaceStartScript(void)
See header file for description.
--------------------------------------------------*/
void ACS_RunRaceStartScript(void)
{
Environment *env = &ACSEnv;
ACSVM::GlobalScope *const global = env->getGlobalScope(0);
ACSVM::HubScope *const hub = global->getHubScope(0);
ACSVM::MapScope *const map = hub->getMapScope(0);
map->scriptStartType(ACS_ST_RACESTART, {});
}
/*--------------------------------------------------
@ -306,29 +324,6 @@ void ACS_RunOvertimeScript(void)
map->scriptStartType(ACS_ST_OVERTIME, {});
}
/*--------------------------------------------------
void ACS_RunEmeraldScript(mobj_t *mo)
See header file for description.
--------------------------------------------------*/
void ACS_RunEmeraldScript(mobj_t *mo)
{
Environment *env = &ACSEnv;
ACSVM::GlobalScope *const global = env->getGlobalScope(0);
ACSVM::HubScope *const hub = global->getHubScope(0);
ACSVM::MapScope *const map = hub->getMapScope(0);
ACSVM::MapScope::ScriptStartInfo scriptInfo;
ThreadInfo info;
P_SetTarget(&info.mo, mo);
scriptInfo.info = &info;
map->scriptStartTypeForced(ACS_ST_EMERALD, scriptInfo);
}
/*--------------------------------------------------
void ACS_RunGameOverScript(void)

View file

@ -138,6 +138,20 @@ void ACS_RunPlayerDeathScript(player_t *player);
void ACS_RunPlayerEnterScript(player_t *player);
/*--------------------------------------------------
void ACS_RunPlayerFinishScript(player_t *player);
Runs the map's special script for a player
finishing (P_DoPlayerExit).
Input Arguments:-
player: The player to run the script for.
Return:-
None
--------------------------------------------------*/
void ACS_RunPlayerFinishScript(player_t *player);
/*--------------------------------------------------
void ACS_RunLapScript(mobj_t *mo, line_t *line);
@ -157,13 +171,13 @@ void ACS_RunLapScript(mobj_t *mo, line_t *line);
/*--------------------------------------------------
void ACS_RunPositionScript(void);
void ACS_RunRaceStartScript(void);
Runs the map's special script for when the level
goes past the POSITION period.
--------------------------------------------------*/
void ACS_RunPositionScript(void);
void ACS_RunRaceStartScript(void);
/*--------------------------------------------------
@ -176,16 +190,6 @@ void ACS_RunPositionScript(void);
void ACS_RunOvertimeScript(void);
/*--------------------------------------------------
void ACS_RunEmeraldScript(mobj_t *mo);
Runs the map's special script for when the
Special Stage Chaos Emerald is collected.
--------------------------------------------------*/
void ACS_RunEmeraldScript(mobj_t *mo);
/*--------------------------------------------------
void ACS_RunGameOverScript(void);

View file

@ -38,11 +38,10 @@ enum acs_scriptType_e
ACS_ST_DEATH = 3, // DEATH: Runs when a player dies.
ACS_ST_ENTER = 4, // ENTER: Runs when a player enters the game; both on start of the level, and when un-spectating.
ACS_ST_LAP = 5, // LAP: Runs when a player's lap increases from crossing the finish line.
ACS_ST_POSITION = 6, // POSITION: Runs when the POSITION period ends.
ACS_ST_OVERTIME = 7, // OVERTIME: Runs when Overtime starts in timed game modes.
ACS_ST_UFO = 8, // UFO: Runs when the UFO Catcher is destroyed in a Special Stage.
ACS_ST_EMERALD = 9, // EMERALD: Runs when the Chaos Emerald is collected in a Special Stage.
ACS_ST_GAMEOVER = 10, // GAMEOVER: Runs when the level ends due to a losing condition and no player has an extra life.
ACS_ST_RACESTART = 6, // RACESTART: Runs when the RACE starts.
ACS_ST_OVERTIME = 8, // OVERTIME: Runs when Overtime starts in timed game modes.
ACS_ST_GAMEOVER = 9, // GAMEOVER: Runs when the level ends due to a losing condition and no player has an extra life.
ACS_ST_FINISH = 10, // FINISH: Runs when a player finishes
};
//

View file

@ -1890,7 +1890,7 @@ void CON_Drawer(void)
if (con_curlines > 0)
CON_DrawConsole();
else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS
else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS || gamestate == GS_BLANCREDITS
|| gamestate == GS_VOTING || gamestate == GS_EVALUATION || gamestate == GS_WAITINGPLAYERS)
CON_DrawHudlines();

View file

@ -57,6 +57,7 @@
#include "k_boss.h"
#include "doomstat.h"
#include "s_sound.h" // sfx_syfail
#include "r_fps.h"
// cl loading screen
#include "v_video.h"
@ -5600,6 +5601,8 @@ boolean TryRunTics(tic_t realtics)
}
else
{
boolean tickInterp = true;
// run the count * tics
while (neededtic > gametic)
{
@ -5627,7 +5630,17 @@ boolean TryRunTics(tic_t realtics)
P_PostLoadLevel();
}
G_Ticker((gametic % NEWTICRATERATIO) == 0);
boolean run = (gametic % NEWTICRATERATIO) == 0;
if (run && tickInterp)
{
// Update old view state BEFORE ticking so resetting
// the old interpolation state from game logic works.
R_UpdateViewInterpolation();
tickInterp = false; // do not update again in sped-up tics
}
G_Ticker(run);
}
if (Playing() && netgame && (gametic % TICRATE == 0))

View file

@ -437,6 +437,12 @@ static bool D_Display(void)
HU_Drawer();
break;
case GS_BLANCREDITS:
F_BlanCreditDrawer();
HU_Erase();
HU_Drawer();
break;
case GS_WAITINGPLAYERS:
// The clientconnect drawer is independent...
if (netgame)

View file

@ -469,6 +469,7 @@ consvar_t cv_kartdebugdistribution = CVAR_INIT ("kartdebugdistribution", "Off",
consvar_t cv_kartdebughuddrop = CVAR_INIT ("kartdebughuddrop", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL);
static CV_PossibleValue_t kartdebugwaypoint_cons_t[] = {{0, "Off"}, {1, "Forwards"}, {2, "Backwards"}, {0, NULL}};
consvar_t cv_kartdebugwaypoints = CVAR_INIT ("kartdebugwaypoints", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, kartdebugwaypoint_cons_t, NULL);
consvar_t cv_kartdebuglap = CVAR_INIT ("kartdebuglap", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, kartdebugwaypoint_cons_t, NULL);
consvar_t cv_kartdebugbotpredict = CVAR_INIT ("kartdebugbotpredict", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL);
consvar_t cv_kartdebugcheckpoint = CVAR_INIT ("kartdebugcheckpoint", "Off", CV_NOSHOWHELP, CV_OnOff, NULL);
@ -5399,7 +5400,7 @@ static void Command_ExitLevel_f(void)
CONS_Printf(M_GetText("This only works in a netgame.\n"));
else if (!(server || (IsPlayerAdmin(consoleplayer))))
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demo.playback)
else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS && gamestate != GS_BLANCREDITS ) || demo.playback)
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
else
SendNetXCmd(XD_EXITLEVEL, NULL, 0);

View file

@ -106,7 +106,7 @@ extern consvar_t cv_votetime;
extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartallowgiveitem, cv_kartdebugdistribution, cv_kartdebughuddrop;
extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector;
extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbotpredict;
extern consvar_t cv_kartdebugwaypoints, cv_kartdebuglap,cv_kartdebugbotpredict;
extern consvar_t cv_itemfinder;

View file

@ -102,7 +102,7 @@ typedef enum
PF_UPDATEMYRESPAWN = 1<<19,
PF_FLIPCAM = 1<<20,
//free = 1<<21,
PF_TRUSTWAYPOINTS = 1<<21, // Do not activate lap cheat prevention next time finish line distance is updated
PF_HITFINISHLINE = 1<<22, // Already hit the finish line this tic
PF_WRONGWAY = 1<<23, // Moving the wrong way with respect to waypoints?
@ -548,6 +548,7 @@ struct player_t
UINT32 distancetofinishprev;
waypoint_t *currentwaypoint;
waypoint_t *nextwaypoint;
UINT16 bigwaypointgap;
tic_t airtime; // Keep track of how long you've been in the air
UINT8 startboost; // (0 to 125) - Boost you get from start of race or respawn drop dash
@ -657,6 +658,9 @@ struct player_t
UINT16 breathTimer; // Holding your breath underwater
UINT8 lastsafelap;
UINT8 lastsafestarpost;
//
SINT8 lives;

View file

@ -1408,6 +1408,8 @@ void readlevelheader(MYFILE *f, char * name)
mapheaderinfo[num]->encorepal = (UINT16)i;
else if (fastcmp(word, "NUMLAPS"))
mapheaderinfo[num]->numlaps = (UINT8)i;
else if (fastcmp(word, "LAPSPERSECTION"))
mapheaderinfo[num]->lapspersection = max((UINT8)i, 1u);
else if (fastcmp(word, "UNLOCKABLE"))
{
if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something

View file

@ -267,7 +267,7 @@ const char *const PLAYERFLAG_LIST[] = {
"UPDATEMYRESPAWN",
"FLIPCAM",
"\x01",
"TRUSTWAYPOINTS",
"HITFINISHLINE", // Already hit the finish line this tic
"WRONGWAY", // Moving the wrong way with respect to waypoints?
@ -1315,6 +1315,7 @@ struct int_const_s const INT_CONST[] = {
{"GS_CUTSCENE",GS_CUTSCENE},
{"GS_DEDICATEDSERVER",GS_DEDICATEDSERVER},
{"GS_WAITINGPLAYERS",GS_WAITINGPLAYERS},
{"GS_BLANCREDITS",GS_BLANCREDITS},
// SRB2Kart
// kartitems_t

View file

@ -412,6 +412,7 @@ struct mapheader_t
UINT16 levelflags; ///< LF_flags: merged booleans into one UINT16 for space, see below
UINT32 typeoflevel; ///< Combination of typeoflevel flags.
UINT8 numlaps; ///< Number of laps in circuit mode, unless overridden.
UINT8 lapspersection; ///< Number of laps per section in hybrid section-circuit maps.
fixed_t gravity; ///< Map-wide gravity.
// Music information

View file

@ -271,7 +271,7 @@ static void F_TitleBGScroll(INT32 scrollspeed)
// =============
// INTRO SCENE
// =============
#define NUMINTROSCENES 1
#define NUMINTROSCENES 2
INT32 intro_scenenum = 0;
INT32 intro_curtime = 0;
@ -279,7 +279,8 @@ const char *introtext[NUMINTROSCENES];
static tic_t introscenetime[NUMINTROSCENES] =
{
4*TICRATE, // KART KR(eW
2*TICRATE, // KART KR(eW
3*TICRATE, // Stuff :)
};
// custom intros
@ -338,6 +339,12 @@ static void F_IntroDrawScene(void)
highres = true;
}
if (intro_scenenum == 1)
{
background = W_CachePatchName("BLANKART", PU_CACHE);
highres = false;
}
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0);
if (background)
@ -372,10 +379,6 @@ void F_IntroTicker(void)
timetonext--;
if (intro_scenenum == 0)
{
if (timetonext <= 0)
{
#if 0 // The necessary apparatus for constructing more elaborate intros...
intro_scenenum++;
F_NewCutscene(introtext[intro_scenenum]);
@ -383,6 +386,42 @@ void F_IntroTicker(void)
wipegamestate = -1;
animtimer = stoptimer = 0;
#endif
if (intro_scenenum == 0)
{
if (timetonext <= 0)
{
if (rendermode != render_none)
{
F_WipeStartScreen();
F_WipeColorFill(31);
F_WipeEndScreen();
F_RunWipe(99, true);
}
intro_scenenum++;
F_NewCutscene(introtext[intro_scenenum]);
timetonext = introscenetime[intro_scenenum];
wipegamestate = -1;
animtimer = stoptimer = 0;
}
if (finalecount == 16)
S_StartSound(NULL, sfx_vroom);
else if (finalecount == 47)
{
// Need to use M_Random otherwise it always uses the same sound
INT32 rskin = M_RandomKey(numskins);
UINT8 rtaunt = M_RandomKey(2);
sfxenum_t rsound = skins[rskin].soundsid[SKSKBST1+rtaunt];
S_StartSound(NULL, rsound);
}
}
if (intro_scenenum == 1)
{
if (timetonext <= 0)
{
if (rendermode != render_none)
{
F_WipeStartScreen();
@ -395,7 +434,7 @@ void F_IntroTicker(void)
{
tic_t nowtime, quittime, lasttime;
nowtime = lasttime = I_GetTime();
quittime = nowtime + NEWTICRATE*2; // Shortened the quit time, used to be 2 seconds
quittime = nowtime + NEWTICRATE; // Shortened the quit time, used to be 2 seconds
while (quittime > nowtime)
{
while (!((nowtime = I_GetTime()) - lasttime))
@ -424,14 +463,10 @@ void F_IntroTicker(void)
D_StartTitle();
return;
}
if (finalecount == 8)
S_StartSound(NULL, sfx_vroom);
else if (finalecount == 47)
if (finalecount == 80)
{
// Need to use M_Random otherwise it always uses the same sound
INT32 rskin = M_RandomKey(numskins);
UINT8 rtaunt = M_RandomKey(2);
sfxenum_t rsound = skins[rskin].soundsid[SKSKBST1+rtaunt];
sfxenum_t rsound = skins[1].soundsid[SKSKWIN];
S_StartSound(NULL, sfx_flgcap);
S_StartSound(NULL, rsound);
}
}
@ -516,43 +551,62 @@ static const char *credits[] = {
"\1Lead Programming",
"Sally \"TehRealSalt\" Cochenour",
"Vivian \"toaster\" Grannell",
"Ronald \"Eidolon\" Kinard",
"James Robert Roman",
"Sean \"Sryder\" Ryder",
"Ehab \"wolfs\" Saeed",
"\"ZarroTsu\"",
"",
"\1Support Programming",
"Colette \"fickleheart\" Bordelon",
"James R.",
"\"Lach\"",
"\"Lat\'\"",
"AJ \"Tyron\" Martinez",
"\"Monster Iestyn\"",
"\"Shuffle\"",
"\"SteelT\"",
"",
"\1External Programming",
"Alam Ed Arias",
"\"alphaRexJames\"",
"\"Ashnal\"",
"\"filpAM\"",
"\"FlykeSpice\"",
"\"Hannu Hanhi\"",
"\"himie\"",
"\"JugadorXEI\"",
"\"Kimberly\"",
"\"Lighto97\"",
"\"Lonsfor\"",
"\"mazmazz\"",
"\"minenice\"",
"\"Shuffle\"",
"\"Snu\"",
"\"X.organic\"",
"",
"\1Lead Artists",
"Desmond \"Blade\" DesJardins",
"\"VelocitOni\"",
"",
"\1Support Artists",
"Sally \"TehRealSalt\" Cochenour",
"\"Chengi\"",
"\"Chrispy\"",
"Sherman \"CoatRack\" DesJardins",
"\"DrTapeworm\"",
"Jesse \"Jeck Jims\" Emerick",
"Wesley \"Charyb\" Gillebaard",
"\"Nev3r\"",
"Vivian \"toaster\" Grannell",
"James \"SeventhSentinel\" Hall",
"\"Lat\'\"",
"\"rairai104n\"",
"\"Tyrannosaur Chao\"",
"\"ZarroTsu\"",
"",
"\1External Artists",
"\"1-Up Mason\"",
"\"Chengi\"",
"\"Chrispy\"",
"\"DirkTheHusky\"",
"\"LJSTAR\"",
"\"MotorRoach\"",
"\"Nev3r\"",
"\"rairai104n\"",
"\"Ritz\"",
"\"Rob\"",
"\"SmithyGNC\"",
@ -569,7 +623,7 @@ static const char *credits[] = {
"\"VAdaPEGA\"",
"\"VelocitOni\"",
"",
"\1Music",
"\1Original Music",
"\"DrTapeworm\"",
"Wesley \"Charyb\" Gillebaard",
"James \"SeventhSentinel\" Hall",
@ -587,7 +641,6 @@ static const char *credits[] = {
"\"DrTapeworm\"",
"Paul \"Boinciel\" Clempson",
"Sherman \"CoatRack\" DesJardins",
"Colette \"fickleheart\" Bordelon",
"Vivian \"toaster\" Grannell",
"\"Gunla\"",
"James \"SeventhSentinel\" Hall",
@ -597,16 +650,19 @@ static const char *credits[] = {
"Sean \"Sryder\" Ryder",
"\"Ryuspark\"",
"\"Simsmagic\"",
"Ivo Solarin",
"\"SP47\"",
"\"TG\"",
"\"Victor Rush Turbo\"",
"\"ZarroTsu\"",
"",
"\1Testing",
"RKH License holders",
"The KCS",
"\"CyberIF\"",
"\"Dani\"",
"Karol \"Fooruman\" D""\x1E""browski", // Dąbrowski, <Sryder> accents in srb2 :ytho:
"\"VirtAnderson\"",
"\"Virt\"",
"",
"\1Special Thanks",
"SEGA",
@ -621,8 +677,8 @@ static const char *credits[] = {
"\"Tyler52\"",
"",
"",
"\1Thank you ",
"\1for playing! ",
"\1Thank you",
"\1for playing!",
NULL
};
@ -635,33 +691,31 @@ static struct {
UINT8 colorize;
} credits_pics[] = {
// We don't have time to be fancy, let's just colorize some item sprites :V
{224, 80+(200* 1), "K_ITJAWZ", SKINCOLOR_CREAMSICLE},
{224, 80+(200* 2), "K_ITSPB", SKINCOLOR_GARDEN},
{224, 80+(200* 3), "K_ITBANA", SKINCOLOR_LILAC},
{224, 80+(200* 4), "K_ITHYUD", SKINCOLOR_DREAM},
{224, 80+(200* 5), "K_ITBHOG", SKINCOLOR_TANGERINE},
{224, 80+(200* 6), "K_ITSHRK", SKINCOLOR_JAWZ},
{224, 80+(200* 7), "K_ITSHOE", SKINCOLOR_MINT},
{224, 80+(200* 8), "K_ITGROW", SKINCOLOR_RUBY},
{224, 80+(200* 9), "K_ITPOGO", SKINCOLOR_SAPPHIRE},
{224, 80+(200*10), "K_ITRSHE", SKINCOLOR_YELLOW},
{224, 80+(200*11), "K_ITORB4", SKINCOLOR_DUSK},
{224, 80+(200*12), "K_ITEGGM", SKINCOLOR_GREEN},
{224, 80+(200*13), "K_ITMINE", SKINCOLOR_BRONZE},
{224, 80+(200*14), "K_ITTHNS", SKINCOLOR_RASPBERRY},
{224, 80+(200*15), "K_ITINV1", SKINCOLOR_GREY},
{224, 80+(216* 1), "K_ITJAWZ", SKINCOLOR_CREAMSICLE},
{224, 80+(216* 2), "K_ITSPB", SKINCOLOR_GARDEN},
{224, 80+(216* 3), "K_ITBANA", SKINCOLOR_LILAC},
{224, 80+(216* 4), "K_ITHYUD", SKINCOLOR_DREAM},
{224, 80+(216* 5), "K_ITBHOG", SKINCOLOR_TANGERINE},
{224, 80+(216* 6), "K_ITSHRK", SKINCOLOR_JAWZ},
{224, 80+(216* 7), "K_ITSHOE", SKINCOLOR_MINT},
{224, 80+(216* 8), "K_ITGROW", SKINCOLOR_RUBY},
{224, 80+(216* 9), "K_ITPOGO", SKINCOLOR_SAPPHIRE},
{224, 80+(216*10), "K_ITRSHE", SKINCOLOR_YELLOW},
{224, 80+(216*11), "K_ITORB4", SKINCOLOR_DUSK},
{224, 80+(216*12), "K_ITEGGM", SKINCOLOR_GREEN},
{224, 80+(216*13), "K_ITMINE", SKINCOLOR_BRONZE},
{224, 80+(216*14), "K_ITTHNS", SKINCOLOR_RASPBERRY},
{224, 80+(216*15), "K_ITINV1", SKINCOLOR_GREY},
// This Tyler52 gag is troublesome
// Alignment should be ((spaces+1 * 100) + (headers+1 * 38) + (lines * 15))
// Current max image spacing: (200*17)
{112, (15*100)+(17*38)+(88*15), "TYLER52", SKINCOLOR_NONE}
// Current max image spacing: (216*17)
{112, (16*100)+(19*38)+(103*15), "TYLER52", SKINCOLOR_NONE},
{0, 0, NULL, SKINCOLOR_NONE}
};
#undef CREDITS_LEFT
#undef CREDITS_RIGHT
static UINT32 credits_height = 0;
static const UINT8 credits_numpics = sizeof(credits_pics)/sizeof(credits_pics[0]) - 1;
void F_StartCredits(void)
{
G_SetGamestate(GS_CREDITS);
@ -692,9 +746,9 @@ void F_StartCredits(void)
void F_CreditDrawer(void)
{
UINT16 i;
fixed_t y = (80<<FRACBITS) - (animtimer<<FRACBITS>>1);
fixed_t y = (80<<FRACBITS) - 5*(animtimer<<FRACBITS)/8;
//V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
// Draw background
V_DrawSciencePatch(0, 0 - FixedMul(32<<FRACBITS, FixedDiv(credbgtimer%TICRATE, TICRATE)), V_SNAPTOTOP, W_CachePatchName("CREDTILE", PU_CACHE), FRACUNIT);
@ -703,7 +757,7 @@ void F_CreditDrawer(void)
V_DrawSciencePatch(320<<FRACBITS, 0 - FixedMul(40<<FRACBITS, FixedDiv(credbgtimer%(TICRATE/2), (TICRATE/2))), V_SNAPTOTOP|V_FLIP, W_CachePatchName("CREDZIGZ", PU_CACHE), FRACUNIT);
// Draw pictures
for (i = 0; i < credits_numpics; i++)
for (i = 0; credits_pics[i].patch; i++)
{
UINT8 *colormap = NULL;
fixed_t sc = FRACUNIT>>1;
@ -717,6 +771,9 @@ void F_CreditDrawer(void)
V_DrawFixedPatch(credits_pics[i].x<<FRACBITS, (credits_pics[i].y<<FRACBITS) - 4*(animtimer<<FRACBITS)/5, sc, 0, W_CachePatchName(credits_pics[i].patch, PU_CACHE), colormap);
}
// Dim the background
//V_DrawFadeScreen();
// Draw credits text on top
for (i = 0; credits[i]; i++)
{
@ -730,11 +787,6 @@ void F_CreditDrawer(void)
V_DrawCreditString((160 - (V_CreditStringWidth(&credits[i][1])>>1))<<FRACBITS, y, 0, &credits[i][1]);
y += 30<<FRACBITS;
break;
case 2:
if (y>>FRACBITS > -10)
V_DrawStringAtFixed((BASEVIDWIDTH-V_StringWidth(&credits[i][1], V_ALLOWLOWERCASE|V_YELLOWMAP))<<FRACBITS>>1, y, V_ALLOWLOWERCASE|V_YELLOWMAP, &credits[i][1]);
y += 12<<FRACBITS;
break;
default:
if (y>>FRACBITS > -10)
V_DrawStringAtFixed(32<<FRACBITS, y, V_ALLOWLOWERCASE, credits[i]);
@ -750,22 +802,7 @@ void F_CreditTicker(void)
{
// "Simulate" the drawing of the credits so that dedicated mode doesn't get stuck
UINT16 i;
fixed_t y = (80<<FRACBITS) - (animtimer<<FRACBITS>>1);
// Calculate credits height to display art properly
if (credits_height == 0)
{
for (i = 0; credits[i]; i++)
{
switch(credits[i][0])
{
case 0: credits_height += 80; break;
case 1: credits_height += 30; break;
default: credits_height += 12; break;
}
}
credits_height = 131*credits_height/80; // account for scroll speeds. This is a guess now, so you may need to update this if you change the credits length.
}
fixed_t y = (80<<FRACBITS) - 5*(animtimer<<FRACBITS)/8;
// Draw credits text on top
for (i = 0; credits[i]; i++)
@ -858,6 +895,165 @@ boolean F_CreditResponder(event_t *event)
return true;
}
// ============
// BLANCREDITS
// ============
static const char *blancredits[] = {
"\1BlanKart",
"\1Credits",
"",
"\1Lead Programming",
"\"NepDisk\"",
"\"GenericHeroGuy\"",
"\"Alug\"",
"\"Indev\"",
"",
"\1Support Programming",
"\"hayaunderscore\" aka \"DeltaKaynx\"",
"\"WumboSpasm\"",
"",
"\1External Programming",
"\"xyzzy\"",
"\"SuperJustinBros\"",
"",
"\1Ring Racers Programming",
"Kart Krew Dev",
"",
"\1New Graphics Creation",
"\"Spee\"",
"\"Jin\"",
"\"NepDisk\"",
"",
"\1Special Thanks",
"\"Sunflower\" aka \"AnimeSonic\"",
"Sunflower's Garden",
"The Moe Mansion and Birdhouse Team",
"",
"",
"\1Thank you",
"\1for playing!",
NULL,
};
void F_BlanStartCredits(void)
{
G_SetGamestate(GS_BLANCREDITS);
// Just in case they're open ... somehow
M_ClearMenus(true);
if (creditscutscene)
{
F_StartCustomCutscene(creditscutscene - 1, false, false);
return;
}
gameaction = ga_nothing;
paused = false;
CON_ToggleOff();
S_StopMusic();
S_StopSounds();
S_ChangeMusicInternal("KMAP04", true);
S_ShowMusicCredit();
finalecount = 0;
animtimer = 0;
timetonext = 2*TICRATE;
}
static void F_DrawDiagCubes(void)
{
INT32 j;
for (j = -4; j < 5; j++)
{
//Up Cubes
V_DrawFill(-((credbgtimer % 128)+1)+(32*j)+32-20, ((credbgtimer % 128)+1)-(32*j), 32, 32, 31|V_SNAPTOBOTTOM|V_SNAPTORIGHT);
V_DrawFill(-((credbgtimer % 128)+1)+(32*j)+32-20, ((credbgtimer % 128)+1)-(32*j), 32, 32, 31|V_SNAPTOTOP|V_SNAPTOLEFT);
V_DrawFill(-((credbgtimer % 128)+1)+(32*j)-20, ((credbgtimer % 128)+1)-(32*j), 32, 32, 31|V_SNAPTOTOP|V_SNAPTOLEFT);
V_DrawFill(-((credbgtimer % 128)+1)+(32*j)-32-20, ((credbgtimer % 128)+1)-(32*j), 32, 32, 31|V_SNAPTOTOP|V_SNAPTOLEFT);
//Down Cubes
V_DrawFill(-((credbgtimer % 128)+1)+(32*j)+320+20, ((credbgtimer % 128)+1)-(32*j)+168, 32, 32, 31|V_SNAPTOBOTTOM|V_SNAPTORIGHT);
V_DrawFill(-((credbgtimer % 128)+1)+(32*j)+288+20, ((credbgtimer % 128)+1)-(32*j)+168, 32, 32, 31|V_SNAPTOBOTTOM|V_SNAPTORIGHT);
V_DrawFill(-((credbgtimer % 128)+1)+(32*j)+288+20, ((credbgtimer % 128)+1)-(32*j)+136, 32, 32, 31|V_SNAPTOBOTTOM|V_SNAPTORIGHT);
}
}
void F_BlanCreditDrawer(void)
{
UINT8 i;
fixed_t y = (80<<FRACBITS) - 5*(animtimer<<FRACBITS)/8;
fixed_t yscroll = FixedMul(32<<FRACBITS, FixedDiv(credbgtimer%TICRATE, TICRATE));
V_DrawSciencePatch(0, 0 - yscroll, V_SNAPTOTOP, W_CachePatchName("CREDTILE", PU_CACHE), FRACUNIT);
V_DrawFillConsoleMap(0,0,vid.width, vid.height,0);
// Draw credits text on top
for (i = 0; blancredits[i]; i++)
{
switch(blancredits[i][0])
{
case 0:
y += 80<<FRACBITS;
break;
case 1:
if (y>>FRACBITS > -20)
V_DrawCenteredStringAtFixed(160<<FRACBITS, y, 0, &blancredits[i][1]);
y += 20<<FRACBITS;
break;
default:
if (y>>FRACBITS > -10)
V_DrawCenteredStringAtFixed(160<<FRACBITS, y, V_ALLOWLOWERCASE, blancredits[i]);
y += 12<<FRACBITS;
break;
}
F_DrawDiagCubes();
if (((y>>FRACBITS) * vid.dupy) > vid.height)
break;
}
}
void F_BlanCreditTicker(void)
{
// "Simulate" the drawing of the credits so that dedicated mode doesn't get stuck
UINT16 i;
fixed_t y = (80<<FRACBITS) - 5*(animtimer<<FRACBITS)/8;
// Draw credits text on top
for (i = 0; blancredits[i]; i++)
{
switch(blancredits[i][0])
{
case 0: y += 80<<FRACBITS; break;
case 1: y += 20<<FRACBITS; break;
default: y += 12<<FRACBITS; break;
}
if (FixedMul(y,vid.dupy) > vid.height)
break;
}
// Do this here rather than in the drawer you doofus! (this is why dedicated mode broke at credits)
if (!blancredits[i] && y <= 120<<FRACBITS && !finalecount)
{
timetonext = 5*TICRATE+1;
finalecount = 5*TICRATE;
}
if (timetonext)
timetonext--;
else
animtimer++;
credbgtimer++;
if (finalecount && --finalecount == 0)
F_StartGameEvaluation();
}
// ============
// EVALUATION
// ============
@ -1345,6 +1541,7 @@ void F_TitleScreenTicker(boolean run)
S_ChangeMusic(menupres[MN_MAIN].musname, menupres[MN_MAIN].mustrack, menupres[MN_MAIN].muslooping);
else
S_ChangeMusicInternal("titles", looptitle);
S_StartSound(NULL, sfx_s23c);
}
}

View file

@ -59,6 +59,9 @@ void F_EndingTicker(void);
void F_CreditTicker(void);
void F_CreditDrawer(void);
void F_BlanCreditTicker(void);
void F_BlanCreditDrawer(void);
void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean resetplayer);
void F_CutsceneDrawer(void);
void F_EndCutScene(void);
@ -74,6 +77,7 @@ void F_StartGameEnd(void);
void F_StartIntro(void);
void F_StartTitleScreen(void);
void F_StartCredits(void);
void F_BlanStartCredits(void);
extern INT32 finalecount;
extern INT32 titlescrollxspeed;

View file

@ -52,6 +52,7 @@
#include "k_bot.h"
#include "k_color.h"
#include "k_follower.h"
#include "k_grandprix.h"
static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}};
consvar_t cv_recordmultiplayerdemos = CVAR_INIT ("netdemo_record", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL);
@ -124,10 +125,12 @@ demoghost *ghosts = NULL;
#define DF_ATTACKSHIFT 1
#define DF_ENCORE 0x40
#define DF_MULTIPLAYER 0x80 // This demo was recorded in multiplayer mode!
#define DF_GRANDPRIX 0x0100
#define DEMO_SPECTATOR 0x01
#define DEMO_KICKSTART 0x02
#define DEMO_SHRINKME 0x04
#define DEMO_BOT 0x08
// For demos
#define ZT_FWD 0x0001
@ -255,13 +258,73 @@ void G_ReadDemoExtraData(void)
{
extradata = READUINT8(demobuf.p);
if (extradata & DXD_RESPAWN)
if (extradata & DXD_JOINDATA)
{
if (players[p].mo)
if (!playeringame[p])
{
// Is this how this should work..?
P_DamageMobj(players[p].mo, NULL, NULL, 1, DMG_INSTAKILL);
G_AddPlayer(p, p);
}
players[p].bot = !!(READUINT8(demobuf.p));
if (players[p].bot)
{
players[p].botvars.difficulty = READUINT8(demobuf.p);
players[p].botvars.diffincrease = READUINT8(demobuf.p); // needed to avoid having to duplicate logic
players[p].botvars.rival = (boolean)READUINT8(demobuf.p);
}
}
if (extradata & DXD_PLAYSTATE)
{
i = READUINT8(demobuf.p);
switch (i) {
case DXD_PST_PLAYING:
if (players[p].spectator == true)
{
if (players[p].bot)
{
players[p].spectator = false;
}
else
{
players[p].pflags |= PF_WANTSTOJOIN;
}
}
//CONS_Printf("player %s is despectating on tic %d\n", player_names[p], leveltime);
break;
case DXD_PST_SPECTATING:
if (players[p].spectator)
{
players[p].pflags &= ~PF_WANTSTOJOIN;
}
else
{
if (players[p].mo)
{
P_DamageMobj(players[p].mo, NULL, NULL, 1, DMG_SPECTATOR);
}
P_SetPlayerSpectator(p);
}
break;
case DXD_PST_LEFT:
CL_RemovePlayer(p, 0);
break;
}
G_ResetViews();
// maybe these are necessary?
K_CheckBumpers();
P_CheckRacers();
}
if (extradata & DXD_NAME)
{
// Name
M_Memcpy(player_names[p],demobuf.p,16);
demobuf.p += 16;
}
if (extradata & DXD_SKIN)
{
@ -295,12 +358,6 @@ void G_ReadDemoExtraData(void)
break;
}
}
if (extradata & DXD_NAME)
{
// Name
M_Memcpy(player_names[p],demobuf.p,16);
demobuf.p += 16;
}
if (extradata & DXD_FOLLOWER)
{
// Set our follower
@ -320,47 +377,13 @@ void G_ReadDemoExtraData(void)
}
}
}
if (extradata & DXD_PLAYSTATE)
if (extradata & DXD_RESPAWN)
{
i = READUINT8(demobuf.p);
switch (i) {
case DXD_PST_PLAYING:
players[p].pflags |= PF_WANTSTOJOIN; // fuck you
//CONS_Printf("player %s is despectating on tic %d\n", player_names[p], leveltime);
break;
case DXD_PST_SPECTATING:
players[p].pflags &= ~PF_WANTSTOJOIN; // double-fuck you
if (!playeringame[p])
{
CL_ClearPlayer(p);
playeringame[p] = true;
G_AddPlayer(p, p);
players[p].spectator = true;
//CONS_Printf("player %s is joining server on tic %d\n", player_names[p], leveltime);
}
else
{
//CONS_Printf("player %s is spectating on tic %d\n", player_names[p], leveltime);
players[p].spectator = true;
if (players[p].mo)
P_DamageMobj(players[p].mo, NULL, NULL, 1, DMG_INSTAKILL);
else
players[p].playerstate = PST_REBORN;
}
break;
case DXD_PST_LEFT:
CL_RemovePlayer(p, 0);
break;
if (players[p].mo)
{
// Is this how this should work..?
P_DamageMobj(players[p].mo, NULL, NULL, 1, DMG_INSTAKILL);
}
G_ResetViews();
// maybe these are necessary?
K_CheckBumpers();
P_CheckRacers();
}
if (extradata & DXD_WEAPONPREF)
{
@ -413,7 +436,44 @@ void G_WriteDemoExtraData(void)
WRITEUINT8(demobuf.p, i);
WRITEUINT8(demobuf.p, demo_extradata[i]);
//if (demo_extradata[i] & DXD_RESPAWN) has no extra data
if (demo_extradata[i] & DXD_JOINDATA)
{
WRITEUINT8(demobuf.p, (UINT8)players[i].bot);
if (players[i].bot)
{
WRITEUINT8(demobuf.p, players[i].botvars.difficulty);
WRITEUINT8(demobuf.p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic
WRITEUINT8(demobuf.p, (UINT8)players[i].botvars.rival);
}
}
if (demo_extradata[i] & DXD_PLAYSTATE)
{
UINT8 pst = DXD_PST_PLAYING;
demo_writerng = 1;
if (!playeringame[i])
{
pst = DXD_PST_LEFT;
}
else if (
players[i].spectator &&
!(players[i].pflags & PF_WANTSTOJOIN) // <= fuck you specifically
)
{
pst = DXD_PST_SPECTATING;
}
WRITEUINT8(demobuf.p, pst);
}
if (demo_extradata[i] & DXD_NAME)
{
// Name
memset(name, 0, 16);
strncpy(name, player_names[i], 16);
M_Memcpy(demobuf.p,name,16);
demobuf.p += 16;
}
if (demo_extradata[i] & DXD_SKIN)
{
// Skin
@ -434,14 +494,6 @@ void G_WriteDemoExtraData(void)
M_Memcpy(demobuf.p,name,16);
demobuf.p += 16;
}
if (demo_extradata[i] & DXD_NAME)
{
// Name
memset(name, 0, 16);
strncpy(name, player_names[i], 16);
M_Memcpy(demobuf.p,name,16);
demobuf.p += 16;
}
if (demo_extradata[i] & DXD_FOLLOWER)
{
// write follower
@ -460,19 +512,7 @@ void G_WriteDemoExtraData(void)
demobuf.p += 16;
}
if (demo_extradata[i] & DXD_PLAYSTATE)
{
demo_writerng = 1;
if (!playeringame[i])
WRITEUINT8(demobuf.p, DXD_PST_LEFT);
else if (
players[i].spectator &&
!(players[i].pflags & PF_WANTSTOJOIN) // <= fuck you specifically
)
WRITEUINT8(demobuf.p, DXD_PST_SPECTATING);
else
WRITEUINT8(demobuf.p, DXD_PST_PLAYING);
}
//if (demo_extradata[i] & DXD_RESPAWN) has no extra data
if (demo_extradata[i] & DXD_WEAPONPREF)
{
WeaponPref_Save(&demobuf.p, i);
@ -1164,23 +1204,27 @@ void G_GhostTicker(void)
UINT16 ziptic = READUINT8(g->p);
UINT8 xziptic = 0;
if (g->done)
{
continue;
}
while (ziptic != DW_END) // Get rid of extradata stuff
{
if (ziptic < MAXPLAYERS)
{
#ifdef DEVELOP
UINT8 playerid = ziptic;
#endif
// We want to skip *any* player extradata because some demos have extradata for bogus players,
// but if there is tic data later for those players *then* we'll consider it invalid.
ziptic = READUINT8(g->p);
if (ziptic & DXD_SKIN)
g->p += 18; // We _could_ read this info, but it shouldn't change anything in record attack...
if (ziptic & DXD_COLOR)
g->p += 16; // Same tbh
if (ziptic & DXD_NAME)
g->p += 16; // yea
if (ziptic & DXD_FOLLOWER)
g->p += 32; // ok (32 because there's both the skin and the colour)
if (ziptic & DXD_JOINDATA)
{
if (READUINT8(g->p) != 0)
I_Error("Ghost is not a record attack ghost (bot JOINDATA)");
}
if (ziptic & DXD_PLAYSTATE)
{
UINT8 playstate = READUINT8(g->p);
@ -1192,6 +1236,14 @@ void G_GhostTicker(void)
;
}
}
if (ziptic & DXD_NAME)
g->p += 16; // yea
if (ziptic & DXD_SKIN)
g->p += 16; // We _could_ read this info, but it shouldn't change anything in record attack...
if (ziptic & DXD_COLOR)
g->p += 16; // Same tbh
if (ziptic & DXD_FOLLOWER)
g->p += 32; // ok (32 because there's both the skin and the colour)
if (ziptic & DXD_WEAPONPREF)
g->p++; // ditto
}
@ -2129,6 +2181,13 @@ void G_BeginRecording(void)
// Save netvar data
CV_SaveDemoVars(&demobuf.p);
if ((demoflags & DF_GRANDPRIX))
{
WRITEUINT8(demobuf.p, grandprixinfo.gamespeed);
WRITEUINT8(demobuf.p, grandprixinfo.masterbots == true);
WRITEUINT8(demobuf.p, grandprixinfo.eventmode);
}
// Save "mapmusrng" used for altmusic selection
WRITEUINT8(demobuf.p, mapmusrng);
@ -2152,8 +2211,17 @@ void G_BeginRecording(void)
i |= DEMO_KICKSTART;
if (player->pflags & PF_SHRINKME)
i |= DEMO_SHRINKME;
if (player->bot == true)
i |= DEMO_BOT;
WRITEUINT8(demobuf.p, i);
if (i & DEMO_BOT)
{
WRITEUINT8(demobuf.p, player->botvars.difficulty);
WRITEUINT8(demobuf.p, player->botvars.diffincrease); // needed to avoid having to duplicate logic
WRITEUINT8(demobuf.p, (UINT8)player->botvars.rival);
}
// Name
memset(name, 0, 16);
strncpy(name, player_names[p], 16);
@ -2806,7 +2874,7 @@ void G_DoPlayDemo(char *defdemoname)
UINT32 randseed;
char msg[1024];
boolean spectator;
boolean spectator, bot;
UINT8 slots[MAXPLAYERS], kartspeed[MAXPLAYERS], kartweight[MAXPLAYERS], numslots = 0;
#if defined(SKIPERRORS) && !defined(DEVELOP)
@ -3083,6 +3151,15 @@ void G_DoPlayDemo(char *defdemoname)
// net var data
CV_LoadDemoVars(&demobuf.p);
memset(&grandprixinfo, 0, sizeof grandprixinfo);
if ((demoflags & DF_GRANDPRIX))
{
grandprixinfo.gp = true;
grandprixinfo.gamespeed = READUINT8(demobuf.p);
grandprixinfo.masterbots = READUINT8(demobuf.p) != 0;
grandprixinfo.eventmode = READUINT8(demobuf.p);
}
// Load "mapmusrng" used for altmusic selection
mapmusrng = READUINT8(demobuf.p);
@ -3135,12 +3212,13 @@ void G_DoPlayDemo(char *defdemoname)
UINT8 flags = READUINT8(demobuf.p);
spectator = !!(flags & DEMO_SPECTATOR);
bot = !!(flags & DEMO_BOT);
if (spectator == true)
if ((spectator || bot))
{
if (modeattacking)
{
snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with spectators, and is thus invalid.\n"), pdemoname);
snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with %s, and is thus invalid.\n"), pdemoname, (bot ? "bots" : "spectators"));
CONS_Alert(CONS_ERROR, "%s", msg);
M_StartMessage(msg, NULL, MM_NOTHING);
Z_Free(pdemoname);
@ -3184,6 +3262,13 @@ void G_DoPlayDemo(char *defdemoname)
K_UpdateShrinkCheat(&players[p]);
if ((players[p].bot = bot) == true)
{
players[p].botvars.difficulty = READUINT8(demobuf.p);
players[p].botvars.diffincrease = READUINT8(demobuf.p); // needed to avoid having to duplicate logic
players[p].botvars.rival = (boolean)READUINT8(demobuf.p);
}
// Name
M_Memcpy(player_names[p],demobuf.p,16);
demobuf.p += 16;
@ -3435,6 +3520,11 @@ void G_AddGhost(char *defdemoname)
p++;
}
if ((flags & DF_GRANDPRIX))
{
p += 3;
}
// Skip mapmusrng
p++;
@ -3449,9 +3539,10 @@ void G_AddGhost(char *defdemoname)
p++; // player number - doesn't really need to be checked, TODO maybe support adding multiple players' ghosts at once
// any invalidating flags?
if ((READUINT8(p) & (DEMO_SPECTATOR)) != 0)
i = READUINT8(p);
if ((i & (DEMO_SPECTATOR|DEMO_BOT)) != 0)
{
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot. (Spectator)\n"), pdemoname);
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot (spectator/bot)\n"), defdemoname);
Z_Free(pdemoname);
Z_Free(buffer);
return;
@ -3656,9 +3747,16 @@ void G_UpdateStaffGhostName(lumpnum_t l)
p++; // stealth
}
if ((flags & DF_GRANDPRIX))
{
p += 3;
}
// Assert first player is in and then read name
if (READUINT8(p) != 0)
goto fail;
if (READUINT8(p) & (DEMO_SPECTATOR|DEMO_BOT))
goto fail;
M_Memcpy(dummystaffname, p,16);
dummystaffname[16] = '\0';

View file

@ -116,13 +116,14 @@ typedef enum
extern UINT8 demo_extradata[MAXPLAYERS];
extern UINT8 demo_writerng;
#define DXD_RESPAWN 0x01 // "respawn" command in console
#define DXD_SKIN 0x02 // skin changed
#define DXD_NAME 0x04 // name changed
#define DXD_COLOR 0x08 // color changed
#define DXD_PLAYSTATE 0x10 // state changed between playing, spectating, or not in-game
#define DXD_FOLLOWER 0x20 // follower was changed
#define DXD_WEAPONPREF 0x40 // netsynced playsim settings were changed
#define DXD_JOINDATA 0x01 // join-specific data
#define DXD_PLAYSTATE 0x02 // state changed between playing, spectating, or not in-game
#define DXD_NAME 0x04 // name changed
#define DXD_SKIN 0x08 // skin changed
#define DXD_COLOR 0x10 // color changed
#define DXD_FOLLOWER 0x20 // follower was changed
#define DXD_RESPAWN 0x40 // "respawn" command in console
#define DXD_WEAPONPREF 0x80 // netsynced playsim settings were changed
#define DXD_PST_PLAYING 0x01
#define DXD_PST_SPECTATING 0x02

View file

@ -1177,7 +1177,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
*kbl = false;
// looking up/down
cmd->aiming += (mlooky<<19)*player_invert*screen_invert;
cmd->aiming += (mlooky<<3)*player_invert*screen_invert;
}
*/
@ -1185,7 +1185,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
INT32 axis = G_PlayerInputAnalog(forplayer, gc_lookup, false) - G_PlayerInputAnalog(forplayer, gc_lookdown, false);
if (axis != 0 && spectating)
cmd->aiming += (axis<<16) * screen_invert;
cmd->aiming += axis * screen_invert;
// spring back if not using keyboard neither mouselookin'
/*
@ -1570,6 +1570,24 @@ boolean G_Responder(event_t *ev)
return true;
}
}
else if (gamestate == GS_BLANCREDITS)
{
if (HU_Responder(ev))
{
hu_keystrokes = true;
return true; // chat ate the event
}
if (F_CreditResponder(ev))
{
// Skip credits for everyone
if (! netgame)
F_StartGameEvaluation();
else if (server || IsPlayerAdmin(consoleplayer))
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
return true;
}
}
else if (gamestate == GS_CONTINUING)
{
return true;
@ -1978,7 +1996,7 @@ void G_Ticker(boolean run)
}
}
D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), false, 1, false, false);
D_MapChange(gamemap, gametype, encoremode, false, 1, false, false);
}
for (i = 0; i < MAXPLAYERS; i++)
@ -2098,6 +2116,12 @@ void G_Ticker(boolean run)
HU_Ticker();
break;
case GS_BLANCREDITS:
if (run)
F_BlanCreditTicker();
HU_Ticker();
break;
case GS_TITLESCREEN:
if (titlemapinaction)
P_Ticker(run);
@ -2260,6 +2284,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
UINT16 nocontrol;
INT32 kickstartaccel;
boolean enteredGame;
UINT8 lastsafelap;
UINT8 lastsafestarpost;
UINT16 bigwaypointgap;
score = players[player].score;
lives = players[player].lives;
@ -2313,6 +2340,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
wanted = 0;
rings = 10;
kickstartaccel = 0;
lastsafelap = 0;
lastsafestarpost = 0;
bigwaypointgap = 0;
nocontrol = 0;
laps = 0;
latestlap = 0;
@ -2380,6 +2410,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
prevcheck = players[player].nextcheck;
pflags |= (players[player].pflags & (PF_STASIS|PF_ELIMINATED|PF_NOCONTEST|PF_LOSTLIFE|PF_FLIPCAM));
lastsafelap = players[player].lastsafelap;
lastsafestarpost = players[player].lastsafestarpost;
bigwaypointgap = players[player].bigwaypointgap;
}
if (!betweenmaps)
@ -2414,6 +2448,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->splitscreenindex = splitscreenindex;
p->spectator = spectator;
p->angleturn = playerangleturn;
p->lastsafelap = lastsafelap;
p->lastsafestarpost = lastsafestarpost;
p->bigwaypointgap = bigwaypointgap;
// save player config truth reborn
p->skincolor = skincolor;
@ -3071,7 +3108,7 @@ void G_FinishExitLevel(void)
// Don't save demos immediately here! Let standings write first
}
else if (gamestate == GS_CREDITS)
else if (gamestate == GS_CREDITS || gamestate == GS_BLANCREDITS)
{
F_StartGameEvaluation();
}

View file

@ -96,7 +96,7 @@ void weaponPrefChange3(void);
void weaponPrefChange4(void);
// mouseaiming (looking up/down with the mouse or keyboard)
#define KB_LOOKSPEED (1<<25)
#define KB_LOOKSPEED (1<<9)
#define MAXPLMOVE (50)
#define SLOWTURNTICS (cv_turnsmooth.value * 3)

View file

@ -43,7 +43,10 @@ typedef enum
// Not fadable
GS_DEDICATEDSERVER, // new state for dedicated server
GS_WAITINGPLAYERS // waiting for players in a net game
GS_WAITINGPLAYERS, // waiting for players in a net game
// New
GS_BLANCREDITS, // BlanKart: Credits for BlanKart
} gamestate_t;
typedef enum

View file

@ -2143,7 +2143,9 @@ void HU_Drawer(void)
|| gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE
|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION
|| gamestate == GS_GAMEEND
|| gamestate == GS_VOTING || gamestate == GS_WAITINGPLAYERS) // SRB2kart
|| gamestate == GS_VOTING || gamestate == GS_WAITINGPLAYERS
|| gamestate == GS_BLANCREDITS
) // SRB2kart
return;
// draw multiplayer rankings

View file

@ -568,9 +568,12 @@ _(ISTB) // instashield layer B
_(PWCL) // Invinc/grow clash VFX
_(ARRO) // player arrows
_(ITEM)
_(ITMO)
_(ITMI)
_(ITEM) // base item
_(ITMO) // Multi-Orbinaut
_(ITMI) // Invincibility
_(ITSN) // Multi-Sneaker
_(ITBA) // Multi-Banana
_(ITJA) // Multi-Jawz
_(ITMN)
_(WANT)

View file

@ -717,17 +717,6 @@ void K_ThunderShieldAttack(mobj_t *actor, fixed_t size)
boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2)
{
if (t1->type == MT_PLAYER)
{
// Bubble Shield already has a hitbox, and it gets
// teleported every tic so the Bubble itself will
// always make contact with other objects.
//
// Therefore, we don't need a second, smaller hitbox
// on the player.
return true;
}
if (t2->type == MT_PLAYER)
{
// Counter desyncs
@ -745,7 +734,7 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2)
return true;
// Player Damage
P_DamageMobj(t2, t1->target, t1, 1, DMG_NORMAL);
P_DamageMobj(t2, ((t1->type == MT_BUBBLESHIELD) ? t1->target : t1), t1, 1, DMG_NORMAL);
if (t1->target->player)
{

View file

@ -4299,6 +4299,11 @@ static void K_DrawWaypointDebugger(void)
if (stplyr != &players[displayplayers[0]]) // only for p1
return;
if (stplyr->bigwaypointgap)
{
V_DrawString(8, 146, 0, va("Auto Respawn Timer: %d", stplyr->bigwaypointgap));
}
V_DrawString(8, 156, 0, va("Current Waypoint ID: %d", K_GetWaypointID(stplyr->currentwaypoint)));
V_DrawString(8, 166, 0, va("Next Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint)));
V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish));

View file

@ -238,6 +238,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartdebugdistribution);
CV_RegisterVar(&cv_kartdebughuddrop);
CV_RegisterVar(&cv_kartdebugwaypoints);
CV_RegisterVar(&cv_kartdebuglap);
CV_RegisterVar(&cv_kartdebugbotpredict);
CV_RegisterVar(&cv_kartdebugcheckpoint);
@ -7025,6 +7026,18 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->checkskip)
player->checkskip--;
if (player->bigwaypointgap && (player->bigwaypointgap > AUTORESPAWN_THRESHOLD || !P_PlayerInPain(player)))
{
player->bigwaypointgap--;
if (!player->bigwaypointgap)
P_KillMobj(player->mo, NULL, NULL, DMG_INSTAKILL);
else if (player->bigwaypointgap == AUTORESPAWN_THRESHOLD)
{
S_StartSound(player->mo, sfx_s26d);
CONS_Printf("You are going the wrong way! You will automatically respawn in 7 seconds.\n");
}
}
if (player->growshrinktimer != 0)
{
if (player->growshrinktimer > 0)
@ -7891,7 +7904,24 @@ static boolean K_SetPlayerNextWaypoint(player_t *player)
continue;
}
bestwaypoint = waypoint->prevwaypoints[i];
if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U))
{
for (size_t j = 0U; j < waypoint->numnextwaypoints; j++)
{
if (!K_GetWaypointIsEnabled(waypoint->nextwaypoints[j]))
{
continue;
}
if (waypoint->nextwaypoints[j] == waypoint)
{
continue;
}
bestwaypoint = waypoint->nextwaypoints[j];
break;
}
}
nextbestdelta = angledelta;
nextbestmomdelta = momdelta;
@ -8117,7 +8147,7 @@ static void K_UpdateDistanceFromFinishLine(player_t *player)
const mapheader_t *mapheader = mapheaderinfo[gamemap - 1];
if ((mapheader->levelflags & LF_SECTIONRACE) == 0U)
{
const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps);
const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps) / mapheader->lapspersection;
player->distancetofinish += numfulllapsleft * K_GetCircuitLength();
}
}
@ -8166,24 +8196,59 @@ static void K_UpdatePlayerWaypoints(player_t *const player)
boolean updaterespawn = K_SetPlayerNextWaypoint(player);
// Update prev value (used for grief prevention code)
K_UpdateDistanceFromFinishLine(player);
player->distancetofinishprev = player->distancetofinish;
K_UpdateDistanceFromFinishLine(player);
// Respawning should be a full reset.
UINT32 delta = u32_delta(player->distancetofinish, player->distancetofinishprev);
if (!player->respawn && delta > distance_threshold)
if (delta > distance_threshold &&
!player->respawn && // Respawning should be a full reset.
old_currentwaypoint != NULL && // So should touching the first waypoint ever.
player->laps != 0 && // POSITION rooms may have unorthodox waypoints to guide bots.
!(player->pflags & PF_TRUSTWAYPOINTS)) // Special exception.
{
CONS_Debug(DBG_GAMELOGIC, "Player %s: waypoint ID %d too far away (%u > %u)\n",
sizeu1(player - players), K_GetWaypointID(player->nextwaypoint), delta, distance_threshold);
#define debug_args "Player %s: waypoint ID %d too far away (%u > %u)\n", \
sizeu1(player - players), K_GetWaypointID(player->nextwaypoint), delta, distance_threshold
if (cv_kartdebuglap.value)
CONS_Printf(debug_args);
else
CONS_Debug(DBG_GAMELOGIC, debug_args);
#undef debug_args
// Distance jump is too great, keep the old waypoints and recalculate distance.
player->currentwaypoint = old_currentwaypoint;
player->nextwaypoint = old_nextwaypoint;
player->distancetofinish = player->distancetofinishprev;
if (!cv_kartdebuglap.value)
{
// Distance jump is too great, keep the old waypoints and old distance.
player->currentwaypoint = old_currentwaypoint;
player->nextwaypoint = old_nextwaypoint;
player->distancetofinish = player->distancetofinishprev;
// Start the auto respawn timer when the distance jumps.
if (!player->bigwaypointgap)
{
player->bigwaypointgap = AUTORESPAWN_TIME;
}
}
}
else
{
// Reset the auto respawn timer if distance changes are back to normal.
if (player->bigwaypointgap && player->bigwaypointgap <= AUTORESPAWN_THRESHOLD + 1)
{
player->bigwaypointgap = 0;
// While the player was in the "bigwaypointgap" state, laps did not change from crossing finish lines.
// So reset the lap back to normal, in case they were able to get behind the line.
player->laps = player->lastsafelap;
if (numbosswaypoints == 0)
{
player->starpostnum = player->lastsafestarpost;
}
}
}
// Respawn point should only be updated when we're going to a nextwaypoint
if ((updaterespawn) &&
(player->bigwaypointgap == 0) &&
(!player->respawn) &&
(player->nextwaypoint != old_nextwaypoint) &&
(K_GetWaypointIsSpawnpoint(player->nextwaypoint)) &&
@ -8192,6 +8257,9 @@ static void K_UpdatePlayerWaypoints(player_t *const player)
if (!(player->pflags & PF_WRONGWAY))
player->grieftime = 0;
player->lastsafelap = player->laps;
player->lastsafestarpost = player->starpostnum;
// Check if respawn is safe. If not then goto next spawnpoint and respawn there.
if (K_SafeRespawnPosition(player->mo))
{
@ -8207,12 +8275,14 @@ static void K_UpdatePlayerWaypoints(player_t *const player)
}
else
{
mobj_t *currentwaypoint = player->currentwaypoint->mobj;
mobj_t *safewaypoint = player->nextwaypoint->mobj;
angle_t respawnangle = R_PointToAngle2(currentwaypoint->x, currentwaypoint->y, safewaypoint->x, safewaypoint->y);
player->starposttime = player->realtime;
player->starpostz = safewaypoint->spawnpoint->z >> FRACBITS;
player->starpostflip = (safewaypoint->flags2 & MF2_OBJECTFLIP);
player->starpostangle = safewaypoint->spawnpoint ? FixedAngle(safewaypoint->spawnpoint->angle * FRACUNIT) : player->mo->angle;
player->starpostangle = respawnangle;
// Then do x and y
player->starpostx = safewaypoint->x >> FRACBITS;
@ -8225,6 +8295,8 @@ static void K_UpdatePlayerWaypoints(player_t *const player)
K_FudgeRespawn(player, player->nextwaypoint);
}
}
player->pflags &= ~PF_TRUSTWAYPOINTS; // clear special exception
}
INT32 K_GetKartRingPower(player_t *player, boolean boosted)
@ -8800,6 +8872,16 @@ void K_UpdateAllPlayerPositions(void)
continue;
}
if (player->respawn > 0 && player->lastsafelap != player->laps)
{
player->laps = player->lastsafelap;
if (numbosswaypoints == 0)
{
player->starpostnum = player->lastsafestarpost;
}
}
K_UpdatePlayerWaypoints(player);
}
@ -10034,9 +10116,9 @@ UINT8 K_GetInvincibilityItemFrame(void)
return ((leveltime % (7*3)) / 3);
}
UINT8 K_GetOrbinautItemFrame(UINT8 count)
UINT8 K_GetMultItemFrame(UINT8 count, UINT8 max)
{
return min(count - 1, 3);
return min(count - 1, max);
}
boolean K_IsSPBInGame(void)
@ -10092,9 +10174,21 @@ void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount)
{
switch (itemType)
{
case KITEM_SNEAKER:
part->sprite = SPR_ITSN;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetMultItemFrame(itemCount, 2);
break;
case KITEM_ORBINAUT:
part->sprite = SPR_ITMO;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(itemCount);
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetMultItemFrame(itemCount, 3);
break;
case KITEM_BANANA:
part->sprite = SPR_ITBA;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetMultItemFrame(itemCount, 3);
break;
case KITEM_JAWZ:
part->sprite = SPR_ITJA;
part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetMultItemFrame(itemCount, 1);
break;
case KITEM_INVINCIBILITY:
part->sprite = SPR_ITMI;

View file

@ -26,6 +26,9 @@ Make sure this matches the actual number of states
#define GROW_SCALE ((3*FRACUNIT)/2)
#define SHRINK_SCALE ((6*FRACUNIT)/8)
#define AUTORESPAWN_TIME (10 * TICRATE)
#define AUTORESPAWN_THRESHOLD (7 * TICRATE)
// Used for respawning checks.
typedef struct respawnresult_s
@ -166,7 +169,7 @@ SINT8 K_Sliptiding(player_t *player);
void K_MoveKartPlayer(player_t *player, boolean onground);
void K_CheckSpectateStatus(boolean considermapreset);
UINT8 K_GetInvincibilityItemFrame(void);
UINT8 K_GetOrbinautItemFrame(UINT8 count);
UINT8 K_GetMultItemFrame(UINT8 count, UINT8 max);
boolean K_IsSPBInGame(void);
// sound stuff for lua

View file

@ -2609,6 +2609,8 @@ static int mapheaderinfo_get(lua_State *L)
lua_pushinteger(L, header->palette);
else if (fastcmp(field,"numlaps"))
lua_pushinteger(L, header->numlaps);
else if (fastcmp(field,"lapspersection"))
lua_pushinteger(L, header->lapspersection);
else if (fastcmp(field,"unlockrequired"))
lua_pushinteger(L, header->unlockrequired);
else if (fastcmp(field,"levelselect"))

View file

@ -388,6 +388,12 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->confirmVictimDelay);
else if (fastcmp(field,"glanceDir"))
lua_pushinteger(L, plr->glanceDir);
else if (fastcmp(field,"breathTimer"))
lua_pushinteger(L, plr->breathTimer);
else if (fastcmp(field,"lastsafelap"))
lua_pushinteger(L, plr->lastsafelap);
else if (fastcmp(field,"laststarpost"))
lua_pushinteger(L, plr->lastsafestarpost);
else if (fastcmp(field,"roundscore"))
plr->roundscore = luaL_checkinteger(L, 3);
else if (fastcmp(field,"marescore"))
@ -799,6 +805,12 @@ static int player_set(lua_State *L)
plr->confirmVictimDelay = luaL_checkinteger(L, 3);
else if (fastcmp(field,"glanceDir"))
plr->glanceDir = luaL_checkinteger(L, 3);
else if (fastcmp(field,"breathTimer"))
plr->breathTimer = luaL_checkinteger(L, 3);
else if (fastcmp(field,"lastsafelap"))
plr->lastsafelap = luaL_checkinteger(L, 3);
else if (fastcmp(field,"lastsafestarpost"))
plr->lastsafestarpost = luaL_checkinteger(L, 3);
else if (fastcmp(field,"roundscore"))
lua_pushinteger(L, plr->roundscore);
else if (fastcmp(field,"marescore"))

View file

@ -225,6 +225,7 @@ static void M_GetAllEmeralds(INT32 choice);
static void M_DestroyRobots(INT32 choice);
//static void M_LevelSelectWarp(INT32 choice);
static void M_Credits(INT32 choice);
static void M_BlanCredits(INT32 choice);
static void M_PandorasBox(INT32 choice);
static void M_EmblemHints(INT32 choice);
static char *M_GetConditionString(condition_t cond);
@ -303,6 +304,10 @@ static void M_AssignJoystick(INT32 choice);
static void M_ChangeControl(INT32 choice);
static void M_ResetControls(INT32 choice);
//camera options menu
menu_t OP_CamOptionsDef;
menu_t OP_Player1CamOptionsDef, OP_Player2CamOptionsDef, OP_Player3CamOptionsDef, OP_Player4CamOptionsDef;
// Video & Sound
menu_t OP_VideoOptionsDef, OP_VideoModeDef;
#ifdef HWRENDER
@ -1071,19 +1076,85 @@ enum
// Prefix: OP_
static menuitem_t OP_MainMenu[] =
{
{IT_SUBMENU|IT_STRING, NULL, "Control Setup...", {.submenu = &OP_ControlsDef}, 10},
{IT_SUBMENU|IT_STRING, NULL, "Control Setup...", {.submenu = &OP_ControlsDef}, 10},
{IT_SUBMENU|IT_STRING, NULL, "Video Options...", {.submenu = &OP_VideoOptionsDef}, 30},
{IT_SUBMENU|IT_STRING, NULL, "Sound Options...", {.submenu = &OP_SoundOptionsDef}, 40},
{IT_SUBMENU|IT_STRING, NULL, "Video Options...", {.submenu = &OP_VideoOptionsDef}, 30},
{IT_SUBMENU|IT_STRING, NULL, "Sound Options...", {.submenu = &OP_SoundOptionsDef}, 40},
{IT_SUBMENU|IT_STRING, NULL, "HUD Options...", {.submenu = &OP_HUDOptionsDef}, 60},
{IT_SUBMENU|IT_STRING, NULL, "Gameplay Options...", {.submenu = &OP_GameOptionsDef}, 70},
{IT_SUBMENU|IT_STRING, NULL, "Server Options...", {.submenu = &OP_ServerOptionsDef}, 80},
{IT_SUBMENU|IT_STRING, NULL, "HUD Options...", {.submenu = &OP_HUDOptionsDef}, 60},
{IT_SUBMENU|IT_STRING, NULL, "Camera Options...", {.submenu = &OP_CamOptionsDef}, 70},
{IT_SUBMENU|IT_STRING, NULL, "Gameplay Options...", {.submenu = &OP_GameOptionsDef}, 80},
{IT_SUBMENU|IT_STRING, NULL, "Server Options...", {.submenu = &OP_ServerOptionsDef}, 90},
{IT_SUBMENU|IT_STRING, NULL, "Data Options...", {.submenu = &OP_DataOptionsDef}, 100},
{IT_SUBMENU|IT_STRING, NULL, "Data Options...", {.submenu = &OP_DataOptionsDef}, 110},
{IT_CALL|IT_STRING, NULL, "Tricks & Secrets (F1)", {.routine = M_Manual}, 120},
{IT_CALL|IT_STRING, NULL, "Play Credits", {.routine = M_Credits}, 130},
{IT_CALL|IT_STRING, NULL, "Tricks & Secrets (F1)", {.routine = M_Manual}, 130},
{IT_CALL|IT_STRING, NULL, "Play Kart Credits", {.routine = M_Credits}, 140},
{IT_CALL|IT_STRING, NULL, "Play BlanKart Credits", {.routine = M_BlanCredits}, 150},
};
static menuitem_t OP_CamOptionsMenu[] =
{
{IT_HEADER, NULL, "Camera Options", {NULL}, 0},
{IT_STRING | IT_CVAR, NULL, "Lagless Camera", {.cvar = &cv_laglesscam}, 20},
{IT_STRING | IT_SUBMENU, NULL, "Player 1 Camera options...", {.submenu = &OP_Player1CamOptionsDef}, 40},
{IT_STRING | IT_SUBMENU, NULL, "Player 2 Camera options...", {.submenu = &OP_Player2CamOptionsDef}, 50},
{IT_STRING | IT_SUBMENU, NULL, "Player 3 Camera options...", {.submenu = &OP_Player3CamOptionsDef}, 60},
{IT_STRING | IT_SUBMENU, NULL, "Player 4 Camera options...", {.submenu = &OP_Player4CamOptionsDef}, 70},
};
static menuitem_t OP_Player1CamOptionsMenu[] =
{
{IT_HEADER, NULL, "Player 1 Camera Options", {NULL}, 0},
{IT_STRING | IT_CVAR, NULL, "Flipcam", {.cvar = &cv_flipcam[0]}, 30},
{IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Distance", {.cvar = &cv_cam_dist[0]}, 40},
{IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Height", {.cvar = &cv_cam_height[0]}, 50},
{IT_STRING | IT_CVAR, NULL, "Camera Speed", {.cvar = &cv_cam_speed[0]}, 60},
//{IT_STRING | IT_CVAR, NULL, "Camera Rotation Speed", {.cvar = &cv_cam_rotspeed[0]}, 70},
{IT_STRING | IT_CVAR, NULL, "Third Person Camera", {.cvar = &cv_chasecam[0]}, 85},
};
static menuitem_t OP_Player2CamOptionsMenu[] =
{
{IT_HEADER, NULL, "Player 2 Camera Options", {NULL}, 0},
{IT_STRING | IT_CVAR, NULL, "Flipcam", {.cvar = &cv_flipcam[1]}, 30},
{IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Distance", {.cvar = &cv_cam_dist[1]}, 40},
{IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Height", {.cvar = &cv_cam_height[1]}, 50},
{IT_STRING | IT_CVAR, NULL, "Camera Speed", {.cvar = &cv_cam_speed[1]}, 60},
//{IT_STRING | IT_CVAR, NULL, "Camera Rotation Speed", {.cvar = &cv_cam_rotspeed[0]}, 70},
{IT_STRING | IT_CVAR, NULL, "Third Person Camera", {.cvar = &cv_chasecam[1]}, 85},
};
static menuitem_t OP_Player3CamOptionsMenu[] =
{
{IT_HEADER, NULL, "Player 3 Camera Options", {NULL}, 0},
{IT_STRING | IT_CVAR, NULL, "Flipcam", {.cvar = &cv_flipcam[2]}, 30},
{IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Distance", {.cvar = &cv_cam_dist[2]}, 40},
{IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Height", {.cvar = &cv_cam_height[2]}, 50},
{IT_STRING | IT_CVAR, NULL, "Camera Speed", {.cvar = &cv_cam_speed[2]}, 60},
//{IT_STRING | IT_CVAR, NULL, "Camera Rotation Speed", {.cvar = &cv_cam_rotspeed[0]}, 70},
{IT_STRING | IT_CVAR, NULL, "Third Person Camera", {.cvar = &cv_chasecam[2]}, 85},
};
static menuitem_t OP_Player4CamOptionsMenu[] =
{
{IT_HEADER, NULL, "Player 4 Camera Options", {NULL}, 0},
{IT_STRING | IT_CVAR, NULL, "Flipcam", {.cvar = &cv_flipcam[3]}, 30},
{IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Distance", {.cvar = &cv_cam_dist[3]}, 40},
{IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Height", {.cvar = &cv_cam_height[3]}, 50},
{IT_STRING | IT_CVAR, NULL, "Camera Speed", {.cvar = &cv_cam_speed[3]}, 60},
//{IT_STRING | IT_CVAR, NULL, "Camera Rotation Speed", {.cvar = &cv_cam_rotspeed[0]}, 70},
{IT_STRING | IT_CVAR, NULL, "Third Person Camera", {.cvar = &cv_chasecam[3]}, 85},
};
static menuitem_t OP_ControlsMenu[] =
@ -1990,6 +2061,12 @@ menu_t OP_HUDOptionsDef =
NULL
};
menu_t OP_CamOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_CamOptionsMenu, &OP_MainDef, 30, 30);
menu_t OP_Player1CamOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_Player1CamOptionsMenu, &OP_CamOptionsDef, 30, 30);
menu_t OP_Player2CamOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_Player2CamOptionsMenu, &OP_CamOptionsDef, 30, 30);
menu_t OP_Player3CamOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_Player3CamOptionsMenu, &OP_CamOptionsDef, 30, 30);
menu_t OP_Player4CamOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_Player4CamOptionsMenu, &OP_CamOptionsDef, 30, 30);
menu_t OP_ChatOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_HUD", OP_ChatOptionsMenu, &OP_HUDOptionsDef, 30, 30);
menu_t OP_GameOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_GAME", OP_GameOptionsMenu, &OP_MainDef, 30, 30);
@ -2364,33 +2441,23 @@ static void M_ChangeCvar(INT32 choice)
choice = (choice<<1) - 1;
if (((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_SLIDER)
||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_INVISSLIDER)
||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_NOMOD))
if (cv->flags & CV_FLOAT)
{
if (cv == &cv_digmusicvolume || cv == &cv_soundvolume)
if (((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_SLIDER)
||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_INVISSLIDER)
||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_NOMOD)
|| !(currentMenu->menuitems[itemOn].status & IT_CV_INTEGERSTEP))
{
choice *= 5;
char s[20];
float n = FIXED_TO_FLOAT(cv->value)+(choice)*(1.0f/16.0f);
sprintf(s,"%ld%s",(long)n,M_Ftrim(n));
CV_Set(cv,s);
}
CV_SetValue(cv,cv->value+choice);
}
else if (cv->flags & CV_FLOAT)
{
char s[20];
sprintf(s,"%f",FIXED_TO_FLOAT(cv->value)+(choice)*(1.0f/16.0f));
CV_Set(cv,s);
else
CV_SetValue(cv,FIXED_TO_FLOAT(cv->value)+(choice));
}
else
{
if (cv == &cv_nettimeout || cv == &cv_jointimeout)
choice *= (TICRATE/7);
else if (cv == &cv_maxsend)
choice *= 512;
else if (cv == &cv_maxping)
choice *= 50;
CV_AddValue(cv,choice);
}
}
static boolean M_ChangeStringCvar(INT32 choice)
@ -2494,7 +2561,9 @@ boolean M_Responder(event_t *ev)
if (dedicated || (demo.playback && demo.title)
|| gamestate == GS_INTRO || gamestate == GS_CUTSCENE || gamestate == GS_GAMEEND
|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION)
|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION
|| gamestate == GS_BLANCREDITS
)
return false;
if (CON_Ready() && gamestate != GS_WAITINGPLAYERS)
@ -6270,9 +6339,10 @@ static void M_Options(INT32 choice)
(void)choice;
// if the player is not admin or server, disable gameplay & server options
OP_MainMenu[4].status = OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
OP_MainMenu[8].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits
OP_MainMenu[9].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits
OP_MainMenu[10].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits
#ifdef HAVE_DISCORDRPC
OP_DataOptionsMenu[4].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data
@ -6773,6 +6843,14 @@ static void M_Credits(INT32 choice)
F_StartCredits();
}
static void M_BlanCredits(INT32 choice)
{
(void)choice;
cursaveslot = -2;
M_ClearMenus(true);
F_BlanStartCredits();
}
/*static void M_CustomLevelSelect(INT32 choice)
{
INT32 ul = skyRoomMenuTranslations[choice-1];

View file

@ -4151,8 +4151,20 @@ static void P_RefreshItemCapsuleParts(mobj_t *mobj)
{
switch (itemType)
{
case KITEM_SNEAKER:
if (mobj->movecount - 1 > K_GetMultItemFrame(mobj->movecount, 2))
count = mobj->movecount;
break;
case KITEM_ORBINAUT: // only display the number when the sprite no longer changes
if (mobj->movecount - 1 > K_GetOrbinautItemFrame(mobj->movecount))
if (mobj->movecount - 1 > K_GetMultItemFrame(mobj->movecount, 3))
count = mobj->movecount;
break;
case KITEM_BANANA: // only display the number when the sprite no longer changes
if (mobj->movecount - 1 > K_GetMultItemFrame(mobj->movecount, 3))
count = mobj->movecount;
break;
case KITEM_JAWZ: // only display the number when the sprite no longer changes
if (mobj->movecount - 1 > K_GetMultItemFrame(mobj->movecount, 1))
count = mobj->movecount;
break;
case KITEM_SUPERRING: // always display the number, and multiply it by 5
@ -6048,25 +6060,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
{
P_SetMobjState(mobj, S_PLAYERARROW_BOX);
switch (mobj->target->player->itemtype)
{
case KITEM_ORBINAUT:
mobj->tracer->sprite = SPR_ITMO;
mobj->tracer->frame = FF_FULLBRIGHT|K_GetOrbinautItemFrame(mobj->target->player->itemamount);
break;
case KITEM_INVINCIBILITY:
mobj->tracer->sprite = SPR_ITMI;
mobj->tracer->frame = FF_FULLBRIGHT|K_GetInvincibilityItemFrame();
break;
case KITEM_SAD:
mobj->tracer->sprite = SPR_ITEM;
mobj->tracer->frame = FF_FULLBRIGHT;
break;
default:
mobj->tracer->sprite = SPR_ITEM;
mobj->tracer->frame = FF_FULLBRIGHT|(mobj->target->player->itemtype);
break;
}
K_UpdateMobjItemOverlay(mobj->tracer, mobj->target->player->itemtype,mobj->target->player->itemamount);
if (mobj->target->player->itemflags & IF_ITEMOUT)
{
@ -6322,6 +6316,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
{
case MT_PLAYER:
/// \todo Have the player's dead body completely finish its animation even if they've already respawned.
if (!mobj->fuse)
{ // Go away.
/// \todo Actually go ahead and remove mobj completely, and fix any bugs and crashes doing this creates. Chasecam should stop moving, and F12 should never return to it.
@ -7410,9 +7405,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
mobj->extravalue2 = mobj->target->player->bubbleblowup;
P_SetScale(mobj, (mobj->destscale = scale));
mobj->flags &= ~(MF_NOCLIPTHING);
P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z);
mobj->flags |= MF_NOCLIPTHING;
break;
}
case MT_FLAMESHIELD:

View file

@ -246,6 +246,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT32(save->p, players[i].distancetofinishprev);
WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].currentwaypoint));
WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].nextwaypoint));
WRITEUINT16(save->p, players[i].bigwaypointgap);
WRITEUINT32(save->p, players[i].airtime);
WRITEUINT8(save->p, players[i].startboost);
@ -348,6 +349,9 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT16(save->p, players[i].breathTimer);
WRITEUINT8(save->p, players[i].lastsafelap);
WRITEUINT8(save->p, players[i].lastsafestarpost);
WRITEUINT8(save->p, players[i].typing_timer);
WRITEUINT8(save->p, players[i].typing_duration);
@ -552,7 +556,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].distancetofinish = READUINT32(save->p);
players[i].distancetofinishprev = READUINT32(save->p);
players[i].currentwaypoint = (waypoint_t *)(size_t)READUINT32(save->p);
players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save->p);
players[i].bigwaypointgap = READUINT16(save->p);
players[i].airtime = READUINT32(save->p);
players[i].startboost = READUINT8(save->p);
@ -655,6 +661,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].breathTimer = READUINT16(save->p);
players[i].lastsafelap = READUINT8(save->p);
players[i].lastsafestarpost = READUINT8(save->p);
players[i].typing_timer = READUINT8(save->p);
players[i].typing_duration = READUINT8(save->p);

View file

@ -427,6 +427,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 num)
mapheaderinfo[num]->palette = UINT16_MAX;
mapheaderinfo[num]->encorepal = UINT16_MAX;
mapheaderinfo[num]->numlaps = NUMLAPS_DEFAULT;
mapheaderinfo[num]->lapspersection = 1;
mapheaderinfo[num]->unlockrequired = -1;
mapheaderinfo[num]->levelselect = 0;
mapheaderinfo[num]->levelflags = 0;
@ -7726,7 +7727,7 @@ static boolean P_SetMapNamespace(void)
else if(fastcmp(tkn, "ringracers"))
{
mapnamespace = MNS_RINGRACERS;
CONS_Alert(CONS_WARNING, "Ring Racers Map deteced. BlanKart has basic support for Ring Racers maps but does not fully support them. Consider converting your map to BlanKart format, consult documentation for help.\n");
CONS_Alert(CONS_WARNING, "Ring Racers Map detected. BlanKart has basic support for Ring Racers maps but does not fully support them. Consider converting your map to BlanKart format, consult documentation for help.\n");
}
else if (fastcmp(tkn, "srb2"))
{

View file

@ -1970,11 +1970,23 @@ void P_SwitchWeather(preciptype_t newWeather)
P_SpawnPrecipitation();
}
static boolean K_IgnoreFinishLine(player_t *player)
{
// If potential lap cheating has been detected, do not
// interact with the finish line at all.
if (player->bigwaypointgap)
return true;
return false;
}
// Passed over the finish line forwards
static void K_HandleLapIncrement(player_t *player)
{
if (player)
{
if (K_IgnoreFinishLine(player))
return;
if (((numbosswaypoints > 0) ? (player->starpostnum >= (numstarposts - (numstarposts/2))) : (player->starpostnum == numstarposts)) || (player->laps == 0))
{
size_t i = 0;
@ -1992,6 +2004,9 @@ static void K_HandleLapIncrement(player_t *player)
player->starposttime = player->realtime;
player->starpostnum = 0;
player->lastsafestarpost = 0;
player->laps++;
if (mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
{
@ -2010,7 +2025,8 @@ static void K_HandleLapIncrement(player_t *player)
player->starpostangle = player->starpostx = player->starposty = player->starpostz = player->starpostflip = 0;
}
player->laps++;
if (!cv_kartdebuglap.value && player->laps == 1)
player->pflags |= PF_TRUSTWAYPOINTS;
K_UpdateAllPlayerPositions();
// Set up lap animation vars

View file

@ -665,13 +665,6 @@ void P_Ticker(boolean run)
players[i].jointime++;
}
if (run)
{
// Update old view state BEFORE ticking so resetting
// the old interpolation state from game logic works.
R_UpdateViewInterpolation();
}
if (objectplacing)
{
if (OP_FreezeObjectplace())
@ -776,30 +769,33 @@ void P_Ticker(boolean run)
// Plays the music after the starting countdown.
else
{
if (!(gametyperules & GTR_FREEROAM) && !(titlemapinaction == TITLEMAP_RUNNING))
if (!(titlemapinaction == TITLEMAP_RUNNING))
{
if (leveltime == starttime-(3*TICRATE))
if (!(gametyperules & GTR_FREEROAM))
{
S_StartSound(NULL, sfx_s3ka7); // 3,
if (leveltime == starttime-(3*TICRATE))
{
S_StartSound(NULL, sfx_s3ka7); // 3,
}
else if ((leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE))
{
S_StartSound(NULL, sfx_s3ka7); // 2, 1,
}
else if (leveltime == starttime)
{
S_StartSound(NULL, sfx_s3kad); // GO!
}
}
else if ((leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE))
{
S_StartSound(NULL, sfx_s3ka7); // 2, 1,
}
else if (leveltime == starttime)
{
S_StartSound(NULL, sfx_s3kad); // GO!
}
}
if (!(gametyperules & GTR_FREEROAM) && leveltime < starttime) // SRB2Kart
S_ChangeMusicInternal((encoremode ? "estart" : "kstart"), false); // yes this will be spammed otherwise encore and some stuff WILL overwrite it
else if (leveltime == starttime) // The GO! sound stops the level start ambience
S_StopMusic();
else if (leveltime == starttime + (TICRATE/2)) // Plays the music after the starting countdown.
{
S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
S_ShowMusicCredit();
if (!(gametyperules & GTR_FREEROAM) && leveltime < starttime) // SRB2Kart
S_ChangeMusicInternal((encoremode ? "estart" : "kstart"), false); // yes this will be spammed otherwise encore and some stuff WILL overwrite it
else if (leveltime == starttime) // The GO! sound stops the level start ambience
S_StopMusic();
else if (leveltime == starttime + (TICRATE/2)) // Plays the music after the starting countdown.
{
S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
S_ShowMusicCredit();
}
}
}
@ -824,7 +820,7 @@ void P_Ticker(boolean run)
if (starttime > introtime && leveltime == starttime)
{
ACS_RunPositionScript();
ACS_RunRaceStartScript();
}
if (timelimitintics > 0 && leveltime == (timelimitintics + starttime + 1))

View file

@ -61,6 +61,8 @@
#include "k_color.h"
#include "k_follower.h"
#include "acs/interface.h"
#ifdef HW3SOUND
#include "hardware/hw3sound.h"
#endif
@ -1324,6 +1326,8 @@ void P_DoPlayerExit(player_t *player)
if (player == &players[consoleplayer])
demo.savebutton = leveltime;
ACS_RunPlayerFinishScript(player);
}
//
@ -1832,8 +1836,6 @@ static void P_3dMovement(player_t *player)
// Do not let the player control movement if not onground.
// SRB2Kart: pogo spring and speed bumps are supposed to control like you're on the ground
onground = (P_IsObjectOnGround(player->mo) || (player->pogospring));
player->aiming = cmd->aiming<<FRACBITS;
// Forward movement
if (!((player->exiting || mapreset) || (P_PlayerInPain(player) && !onground)))
@ -1954,24 +1956,60 @@ static void P_3dMovement(player_t *player)
boolean P_CanPlayerTurn(player_t *player, ticcmd_t *cmd)
{
if (player->spectator || objectplacing) // Spectators come first.
if (player->spectator || objectplacing)
{
// Spectators come first.
return true;
}
if (!(gametyperules & GTR_FREEROAM) && !(leveltime > starttime)) // You can't move yet.
return false;
if ((cmd->buttons & BT_ACCELERATE) && (cmd->buttons & BT_BRAKE)) // You are rubberburn turning.
return true;
if (player->respawn) // You are Respawning.
return true;
if (player->mo && player->speed == 0) // You need to be moving.
if (((gametyperules & GTR_FREEROAM) || (leveltime > starttime))
&& (cmd->buttons & BT_ACCELERATE)
&& (cmd->buttons & BT_BRAKE))
{
// You are rubberburn turning.
return true;
}
if (player->respawn)
{
// You are Respawning.
return true;
}
if (player->mo && player->speed == 0)
{
// You need to be moving.
return false;
}
return true;
}
static void P_UpdatePlayerAiming(player_t *player)
{
ticcmd_t *cmd = &player->cmd;
int i;
if (!cv_allowmlook.value || player->spectator == false)
{
player->aiming = 0;
}
else
{
player->aiming += (cmd->aiming << TICCMD_REDUCE);
player->aiming = G_ClipAimingPitch((INT32*) &player->aiming);
}
for (i = 0; i <= r_splitscreen; i++)
{
if (player == &players[displayplayers[i]])
{
localaiming[i] = player->aiming;
break;
}
}
}
//
// P_UpdatePlayerAngle
//
@ -1983,7 +2021,6 @@ static void P_UpdatePlayerAngle(player_t *player)
boolean add_delta = true;
ticcmd_t *cmd = &player->cmd;
angle_t anglechange = player->angleturn;
int i;
// Kart: store the current turn range for later use
if (P_CanPlayerTurn(player, cmd))
@ -2031,14 +2068,7 @@ static void P_UpdatePlayerAngle(player_t *player)
player->angleturn = anglechange;
player->mo->angle = player->angleturn;
for (i = 0; i <= r_splitscreen; i++)
{
if (player == &players[displayplayers[i]])
{
localaiming[i] = player->aiming;
break;
}
}
P_UpdatePlayerAiming(player);
}
static void P_UpdateBotAngle(player_t* player)
@ -2058,6 +2088,7 @@ static void P_SpectatorMovement(player_t *player)
ticcmd_t *cmd = &player->cmd;
player->mo->angle = cmd->angle<<16;
P_UpdatePlayerAiming(player);
ticruned++;
if (!(cmd->flags & TICCMD_RECEIVED))
@ -2586,6 +2617,24 @@ static void P_DeathThink(player_t *player)
else
player->karthud[khud_timeovercam] = 0;
// Set players next respawn point to the next waypoint
// If they die while still in respawn state for extra safety.
if (player->nextwaypoint && player->respawn > 0)
{
mobj_t *currentwaypoint = player->currentwaypoint->mobj;
mobj_t *safewaypoint = player->nextwaypoint->mobj;
angle_t respawnangle = R_PointToAngle2(currentwaypoint->x, currentwaypoint->y, safewaypoint->x, safewaypoint->y);
player->starposttime = player->realtime;
player->starpostz = safewaypoint->spawnpoint->z >> FRACBITS;
player->starpostflip = (safewaypoint->flags2 & MF2_OBJECTFLIP);
player->starpostangle = respawnangle;
// Then do x and y
player->starpostx = safewaypoint->x >> FRACBITS;
player->starposty = safewaypoint->y >> FRACBITS;
}
K_KartPlayerHUDUpdate(player);
if (player->pflags & PF_NOCONTEST)

View file

@ -2539,6 +2539,12 @@ void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, con
V_DrawThinStringAtFixed(x, y, option, string);
}
void V_DrawCenteredStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
{
x -= (V_StringWidth(string, option) / 2) * FRACUNIT;
V_DrawStringAtFixed(x, y, option, string);
}
// Draws a number using the PING font thingy.
// TODO: Merge number drawing functions into one with "font name" selection.

View file

@ -281,6 +281,9 @@ void V_DrawCenteredSmallString(INT32 x, INT32 y, INT32 option, const char *strin
void V_DrawCenteredSmallStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string);
void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *string);
// Draw a centered string.
void V_DrawCenteredStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string);
// draw a string using the tny_font
#define V_DrawThinString( x,y,option,string ) \
V__DrawDupxString (x,y,FRACUNIT,option,TINY_FONT,string)