mirror of
				https://github.com/KartKrewDev/RingRacers.git
				synced 2025-10-30 08:01:28 +00:00 
			
		
		
		
	Compare commits
	
		
			147 commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | c2c3ae63a7 | ||
|   | 721d5630a1 | ||
|   | 200e7b4014 | ||
|   | d4c2235e92 | ||
|   | b9df8963cd | ||
|   | 719923e0cf | ||
|   | e864ba5255 | ||
|   | b0e1b14c9b | ||
|   | 15f66de4af | ||
|   | 6d344ad118 | ||
|   | 5f0121cca8 | ||
|   | 36de3285bd | ||
|   | ecb1927cf8 | ||
|   | c28733f2f5 | ||
|   | e63ad4a681 | ||
|   | c087c8bacc | ||
|   | 8ce90ff211 | ||
|   | b4c597161b | ||
|   | 2b7e1384ed | ||
|   | 176713d243 | ||
|   | d363c71007 | ||
|   | fd27994c48 | ||
|   | 9244b9148e | ||
|   | 5d697e378f | ||
|   | 0e431cd334 | ||
|   | 21a3489bcb | ||
|   | 039ba6c3dc | ||
|   | de0fc7d3be | ||
|   | fcb7d38dce | ||
|   | f984ecc5d7 | ||
|   | 4c2e540451 | ||
|   | 59830a4ff0 | ||
|   | af61408029 | ||
|   | 668072c285 | ||
|   | 687bfb0e1d | ||
|   | 121ac80b43 | ||
|   | c9dce813e3 | ||
|   | e6b21388bd | ||
|   | 7ac5a45979 | ||
|   | ea8acf33aa | ||
|   | add76b17aa | ||
|   | ba8bbc643c | ||
|   | 9087914dca | ||
|   | f0f14ae91e | ||
|   | b10b845a05 | ||
|   | 8a55463587 | ||
|   | c2f68ba92b | ||
|   | 5b0351524f | ||
|   | 3e058ed7cf | ||
|   | c42de4ec8c | ||
|   | 046afb223d | ||
|   | 35da317d62 | ||
|   | 2f189ca6f0 | ||
|   | 5a622e6ce0 | ||
|   | 3d5639c864 | ||
|   | 7491e40e0f | ||
|   | aef864345f | ||
|   | 07f80f05df | ||
|   | c73c02aac9 | ||
|   | f09950ddd5 | ||
|   | 70675476e5 | ||
|   | 68413fde44 | ||
|   | 0b841b486f | ||
|   | c009236107 | ||
|   | 2928f84d9a | ||
|   | 09c6ee99f2 | ||
|   | 3244e42177 | ||
|   | a1e903fc72 | ||
|   | fa8c2aac86 | ||
|   | d0938e1dc9 | ||
|   | 537a44871a | ||
|   | 71a1252ec5 | ||
|   | 4bb035c7a5 | ||
|   | 131ce41657 | ||
|   | f7afea7abb | ||
|   | 188d168ace | ||
|   | 66a7f40800 | ||
|   | 114b01a516 | ||
|   | 17ff0fea89 | ||
|   | 17556495f6 | ||
|   | 93918b7bf2 | ||
|   | 3dc57d5908 | ||
|   | 303401aea5 | ||
|   | fda4e6542e | ||
|   | f67c53e130 | ||
|   | 2a7b4c1396 | ||
|   | feb9787187 | ||
|   | 8194b0ee8d | ||
|   | 653393e5a9 | ||
|   | b7a50f1123 | ||
|   | 54e9534615 | ||
|   | 151b91f204 | ||
|   | cb48d53f02 | ||
|   | c224b38685 | ||
|   | 19600c2589 | ||
|   | f59dc1e3a2 | ||
|   | b8769f14de | ||
|   | 725882ab76 | ||
|   | 5019203872 | ||
|   | 773736da46 | ||
|   | b7437a6565 | ||
|   | 6fcfd452aa | ||
|   | 3e1311473d | ||
|   | 1737db47db | ||
|   | 65f388de3a | ||
|   | e11751ddf7 | ||
|   | 4faf85eab4 | ||
|   | d3163d73cf | ||
|   | 06cf1b914a | ||
|   | 3e185efa44 | ||
|   | 56533a1876 | ||
|   | d37590f84c | ||
|   | c40c9a87d3 | ||
|   | eaf2e34efb | ||
|   | 99542a5b00 | ||
|   | 7e618b888e | ||
|   | 507a4b6c91 | ||
|   | fd248e7e58 | ||
|   | 2fe37dc996 | ||
|   | bfa4b11b6d | ||
|   | 1d3eda5926 | ||
|   | 2ddc4c4867 | ||
|   | e6da621dec | ||
|   | 7f568779d8 | ||
|   | 3befb83abf | ||
|   | d5154314e5 | ||
|   | bbe55cda4b | ||
|   | 3b0e3ec75c | ||
|   | 3cadaf70cc | ||
|   | 3150c1f62e | ||
|   | 15413bf6d0 | ||
|   | 90ec96a2b0 | ||
|   | 50d83e4b6d | ||
|   | 6182b83518 | ||
|   | 6fa09928c8 | ||
|   | 9fb3719a8d | ||
|   | a02f1f7506 | ||
|   | 3425727ca6 | ||
|   | 0aad29ff68 | ||
|   | 83a0ca6a1f | ||
|   | c7d137de5c | ||
|   | 78e4724e2d | ||
|   | 7e0fa35b7f | ||
|   | 71dc52d732 | ||
|   | 14c82a9f5a | ||
|   | d782a6fdb7 | ||
|   | 2270813eee | 
					 71 changed files with 2397 additions and 411 deletions
				
			
		|  | @ -1,3 +1,7 @@ | |||
| # Have you already searched the issue tracker for issues similar to yours?  | ||||
| 
 | ||||
| (Use the search bar on https://gitlab.com/kart-krew-dev/ring-racers/-/issues - duplicate reports make it tough for us to keep track of things.) | ||||
| 
 | ||||
| # What version of Ring Racers are you playing? | ||||
| 
 | ||||
| (Replace this text with the version number. You can see this on the title screen at the bottom-left corner.) | ||||
|  |  | |||
|  | @ -130,6 +130,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 | |||
| 	lua_itemroulettelib.c | ||||
| 	lua_respawnvarslib.c | ||||
| 	lua_waypointslib.c | ||||
| 	lua_grandprixlib.c | ||||
| 	lua_profile.cpp | ||||
| 	k_kart.c | ||||
| 	k_respawn.c | ||||
|  |  | |||
|  | @ -1654,6 +1654,32 @@ bool CallFunc_PlayerSkin(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM:: | |||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| /*--------------------------------------------------
 | ||||
| 	bool CallFunc_PlayerSkinRealName(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) | ||||
| 
 | ||||
| 		Returns the activating player's skin real name. | ||||
| --------------------------------------------------*/ | ||||
| bool CallFunc_PlayerSkinRealName(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) | ||||
| { | ||||
| 	Environment *env = &ACSEnv; | ||||
| 	auto info = &static_cast<Thread *>(thread)->info; | ||||
| 
 | ||||
| 	(void)argV; | ||||
| 	(void)argC; | ||||
| 
 | ||||
| 	if ((info != NULL) | ||||
| 		&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false) | ||||
| 		&& (info->mo->player != NULL)) | ||||
| 	{ | ||||
| 		UINT16 skin = info->mo->player->skin; | ||||
| 		thread->dataStk.push(~env->getString( skins[skin]->realname )->idx); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	thread->dataStk.push(0); | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| /*--------------------------------------------------
 | ||||
| 	bool CallFunc_PlayerBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) | ||||
| 
 | ||||
|  | @ -2409,6 +2435,8 @@ bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM | |||
| bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) | ||||
| { | ||||
| 	ACSVM::MapScope *map = thread->scopeMap; | ||||
| 	ACSVM::String *tuneStr = nullptr; | ||||
| 	const char *tune = nullptr; | ||||
| 
 | ||||
| 	// 0: str tune - id for the tune to play
 | ||||
| 	// 1: str song - lump name for the song to map to
 | ||||
|  | @ -2419,6 +2447,16 @@ bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM:: | |||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	tuneStr = map->getString(argV[0]); | ||||
| 	tune = tuneStr->str; | ||||
| 
 | ||||
| 	// Do not allow ACS to remap Stereo Mode tunes.
 | ||||
| 	if (strlen(tune) > 5 | ||||
| 	    && toupper(tune[0]) == 'S' && toupper(tune[1]) == 'T' && toupper(tune[2]) == 'E' && toupper(tune[3]) == 'R' && toupper(tune[4]) == 'E') | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	Music_Remap(map->getString(argV[0])->str, map->getString(argV[1])->str); | ||||
| 
 | ||||
| 	return false; | ||||
|  |  | |||
|  | @ -76,6 +76,7 @@ bool CallFunc_CountPushables(ACSVM::Thread *thread, const ACSVM::Word *argV, ACS | |||
| bool CallFunc_HaveUnlockableTrigger(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); | ||||
| bool CallFunc_HaveUnlockable(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); | ||||
| bool CallFunc_PlayerSkin(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); | ||||
| bool CallFunc_PlayerSkinRealName(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); | ||||
| bool CallFunc_PlayerBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); | ||||
| bool CallFunc_PlayerLosing(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); | ||||
| bool CallFunc_PlayerExiting(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); | ||||
|  |  | |||
|  | @ -177,6 +177,7 @@ Environment::Environment() | |||
| 	addFuncDataACS0( 318, addCallFunc(CallFunc_CheckTutorialChallenge)); | ||||
| 	addFuncDataACS0( 319, addCallFunc(CallFunc_PlayerLosing)); | ||||
| 	addFuncDataACS0( 320, addCallFunc(CallFunc_PlayerExiting)); | ||||
| 	addFuncDataACS0( 321, addCallFunc(CallFunc_PlayerSkinRealName)); | ||||
| 
 | ||||
| 	addFuncDataACS0( 500, addCallFunc(CallFunc_CameraWait)); | ||||
| 	addFuncDataACS0( 501, addCallFunc(CallFunc_PodiumPosition)); | ||||
|  |  | |||
|  | @ -1008,7 +1008,7 @@ consvar_t cv_dummyprofilerumble = MenuDummy("dummyprofilerumble", "On").on_off() | |||
| consvar_t cv_dummyscramble = MenuDummy("dummyscramble", "Random").values({{0, "Random"}, {1, "Points"}}); | ||||
| 
 | ||||
| void CV_SPBAttackChanged(void); | ||||
| consvar_t cv_dummyspbattack = MenuDummy("dummyspbattack", "Off").on_off().onchange(CV_SPBAttackChanged); | ||||
| consvar_t cv_dummyspbattack = MenuDummy("dummyspbattack", "Off").on_off().onchange_noinit(CV_SPBAttackChanged); | ||||
| 
 | ||||
| consvar_t cv_dummyspectate = MenuDummy("dummyspectate", "Spectator").values({{0, "Spectator"}, {1, "Playing"}}); | ||||
| 
 | ||||
|  |  | |||
|  | @ -105,6 +105,7 @@ boolean server = true; // true or false but !server == client | |||
| #define client (!server) | ||||
| boolean nodownload = false; | ||||
| boolean serverrunning = false; | ||||
| boolean connectedtodedicated = false; | ||||
| INT32 serverplayer = 0; | ||||
| char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support)
 | ||||
| 
 | ||||
|  | @ -1271,6 +1272,7 @@ static boolean SV_SendServerConfig(INT32 node) | |||
| 	netbuffer->u.servercfg.gamestate = (UINT8)gamestate; | ||||
| 	netbuffer->u.servercfg.gametype = (UINT8)gametype; | ||||
| 	netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame; | ||||
| 	netbuffer->u.servercfg.dedicated = (boolean)dedicated; | ||||
| 
 | ||||
| 	netbuffer->u.servercfg.maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value)); | ||||
| 	netbuffer->u.servercfg.allownewplayer = cv_allownewplayer.value; | ||||
|  | @ -2247,7 +2249,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic | |||
| #ifdef HAVE_THREADS | ||||
| 			I_lock_mutex(&k_menu_mutex); | ||||
| #endif | ||||
| 			M_UpdateMenuCMD(0, true); | ||||
| 			M_UpdateMenuCMD(0, true, false); | ||||
| 
 | ||||
| 			if (cl_mode == CL_CONFIRMCONNECT) | ||||
| 			{ | ||||
|  | @ -2502,6 +2504,7 @@ static void Command_connect(void) | |||
| 	// we don't request a restart unless the filelist differs
 | ||||
| 
 | ||||
| 	server = false; | ||||
| 	connectedtodedicated = false; | ||||
| 
 | ||||
| 	// Get the server node.
 | ||||
| 	if (netgame) | ||||
|  | @ -2729,6 +2732,19 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) | |||
| 
 | ||||
| 	K_CheckBumpers(); | ||||
| 	P_CheckRacers(); | ||||
| 	 | ||||
| 	// Reset map headers' justPlayed and anger records
 | ||||
| 	// when there are no players in a dedicated server.
 | ||||
| 	// Otherwise maps get angry at newly-joined players
 | ||||
| 	// that don't deserve it.
 | ||||
| 	if (dedicated && D_NumPlayers() == 0) | ||||
| 	{ | ||||
| 		for (INT32 i = 0; i < nummapheaders; i++) | ||||
| 		{ | ||||
| 			mapheaderinfo[i]->justPlayed = 0; | ||||
| 			mapheaderinfo[i]->anger = 0; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void CL_Reset(void) | ||||
|  | @ -2748,6 +2764,7 @@ void CL_Reset(void) | |||
| 	multiplayer = false; | ||||
| 	servernode = 0; | ||||
| 	server = true; | ||||
| 	connectedtodedicated = false; | ||||
| 	doomcom->numnodes = 1; | ||||
| 	doomcom->numslots = 1; | ||||
| 	SV_StopServer(); | ||||
|  | @ -4261,6 +4278,11 @@ boolean Playing(void) | |||
| 	return (server && serverrunning) || (client && cl_mode == CL_CONNECTED); | ||||
| } | ||||
| 
 | ||||
| boolean InADedicatedServer(void) | ||||
| { | ||||
| 	return Playing() && (dedicated || connectedtodedicated); | ||||
| } | ||||
| 
 | ||||
| boolean SV_SpawnServer(void) | ||||
| { | ||||
| #ifdef TESTERS | ||||
|  | @ -4363,6 +4385,7 @@ void SV_StartSinglePlayerServer(INT32 dogametype, boolean donetgame) | |||
| { | ||||
| 	INT32 lastgametype = gametype; | ||||
| 	server = true; | ||||
| 	connectedtodedicated = false; | ||||
| 	multiplayer = (modeattacking == ATTACKING_NONE); | ||||
| 	joinedIP[0] = '\0';	// Make sure to empty this so that we don't save garbage when we start our own game. (because yes we use this for netgames too....)
 | ||||
| 
 | ||||
|  | @ -4989,6 +5012,7 @@ static void HandlePacketFromAwayNode(SINT8 node) | |||
| 				G_SetGametype(netbuffer->u.servercfg.gametype); | ||||
| 
 | ||||
| 				modifiedgame = netbuffer->u.servercfg.modifiedgame; | ||||
| 				connectedtodedicated = netbuffer->u.servercfg.dedicated; | ||||
| 
 | ||||
| 				memcpy(server_context, netbuffer->u.servercfg.server_context, 8); | ||||
| 
 | ||||
|  |  | |||
|  | @ -231,6 +231,7 @@ struct serverconfig_pak | |||
| 
 | ||||
| 	UINT8 gametype; | ||||
| 	UINT8 modifiedgame; | ||||
| 	boolean dedicated; | ||||
| 
 | ||||
| 	char server_context[8]; // Unique context id, generated at server startup.
 | ||||
| 
 | ||||
|  | @ -407,7 +408,7 @@ struct resultsall_pak | |||
| 
 | ||||
| struct say_pak | ||||
| { | ||||
| 	char message[HU_MAXMSGLEN]; | ||||
| 	char message[HU_MAXMSGLEN + 1]; | ||||
| 	UINT8 target; | ||||
| 	UINT8 flags; | ||||
| 	UINT8 source; | ||||
|  | @ -465,7 +466,7 @@ struct doomdata_t | |||
| 		client3cmd_pak client3pak;          //         264 bytes(?)
 | ||||
| 		client4cmd_pak client4pak;          //         324 bytes(?)
 | ||||
| 		servertics_pak serverpak;           //      132495 bytes (more around 360, no?)
 | ||||
| 		serverconfig_pak servercfg;         //         773 bytes
 | ||||
| 		serverconfig_pak servercfg;         //         777 bytes
 | ||||
| 		UINT8 textcmd[MAXTEXTCMD+2];        //       66049 bytes (wut??? 64k??? More like 258 bytes...)
 | ||||
| 		char filetxpak[sizeof (filetx_pak)];//         139 bytes
 | ||||
| 		char fileack[sizeof (fileack_pak)]; | ||||
|  | @ -558,6 +559,7 @@ extern boolean server; | |||
| extern boolean serverrunning; | ||||
| #define client (!server) | ||||
| extern boolean dedicated; // For dedicated server
 | ||||
| extern boolean connectedtodedicated; // Client that is connected to a dedicated server.
 | ||||
| extern UINT16 software_MAXPACKETLENGTH; | ||||
| extern boolean acceptnewnode; | ||||
| extern SINT8 servernode; | ||||
|  | @ -670,6 +672,7 @@ void CL_UpdateServerList(void); | |||
| void CL_TimeoutServerList(void); | ||||
| // Is there a game running
 | ||||
| boolean Playing(void); | ||||
| boolean InADedicatedServer(void); | ||||
| 
 | ||||
| // Advance client-to-client pubkey verification flow
 | ||||
| void UpdateChallenges(void); | ||||
|  |  | |||
|  | @ -107,17 +107,17 @@ extern "C" consvar_t cv_lua_profile, cv_menuframeskip; | |||
| /* Manually defined asset hashes
 | ||||
|  */ | ||||
| 
 | ||||
| #define ASSET_HASH_BIOS_PK3						"5f9093a5c6abfb77ab518ac530312564" | ||||
| #define ASSET_HASH_BIOS_PK3						"36201c4690256d133dff7d3879436dff" | ||||
| #define ASSET_HASH_SCRIPTS_PK3					"56be3c47192870c3265f19cf024e860e" | ||||
| #define ASSET_HASH_GFX_PK3						"24a59ebaa74f253dbec55b00328accb9" | ||||
| #define ASSET_HASH_TEXTURES_GENERAL_PK3			"609b683d3efc291ea28dd4e50d731f34" | ||||
| #define ASSET_HASH_TEXTURES_SEGAZONES_PK3		"9c39dfc868680ffd5f44a7269971e419" | ||||
| #define ASSET_HASH_TEXTURES_ORIGINALZONES_PK3	"6c49b9b6e273efd79c51c25fed85b290" | ||||
| #define ASSET_HASH_GFX_PK3						"9e91306851cb6619124b37533cfbf029" | ||||
| #define ASSET_HASH_TEXTURES_GENERAL_PK3			"3b81c0645b9e0580c1675f2eb70c4250" | ||||
| #define ASSET_HASH_TEXTURES_SEGAZONES_PK3		"2e87cb9dddae7d32656932fdad32b22f" | ||||
| #define ASSET_HASH_TEXTURES_ORIGINALZONES_PK3	"f15f974dbd17c9ce1b60bf31cf12d246" | ||||
| #define ASSET_HASH_CHARS_PK3					"5c8c34c5623acf984e3f654da4509126" | ||||
| #define ASSET_HASH_FOLLOWERS_PK3				"4b61428e5f2ec806de398de8a5fba5f0" | ||||
| #define ASSET_HASH_MAPS_PK3						"bafab7e445c762de568294016ff6eefb" | ||||
| #define ASSET_HASH_MAPS_PK3						"d744ac9747e078220a986ab295721182" | ||||
| #define ASSET_HASH_UNLOCKS_PK3					"a4de35ba9f83829ced44dfc1316ba33e" | ||||
| #define ASSET_HASH_STAFFGHOSTS_PK3				"a05c77ba7cfabfa453fe7b1784926ebd" | ||||
| #define ASSET_HASH_STAFFGHOSTS_PK3				"4248d1fb6eb14c6b359f739c118249cc" | ||||
| #define ASSET_HASH_SHADERS_PK3					"bc0b47744d457956db2ee9ea00f59eff" | ||||
| #ifdef USE_PATCH_FILE | ||||
| #define ASSET_HASH_PATCH_PK3					"00000000000000000000000000000000" | ||||
|  | @ -343,7 +343,7 @@ void D_ProcessEvents(boolean callresponders) | |||
| 	// Update menu CMD
 | ||||
| 	for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) | ||||
| 	{ | ||||
| 		M_UpdateMenuCMD(i, false); | ||||
| 		M_UpdateMenuCMD(i, false, chat_keydown); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -864,6 +864,7 @@ void D_SRB2Loop(void) | |||
| 
 | ||||
| 	if (dedicated) | ||||
| 		server = true; | ||||
| 	connectedtodedicated = dedicated; | ||||
| 
 | ||||
| 	// Pushing of + parameters is now done back in D_SRB2Main, not here.
 | ||||
| 
 | ||||
|  | @ -1597,6 +1598,7 @@ void D_SRB2Main(void) | |||
| 
 | ||||
| 	// for dedicated server
 | ||||
| 	dedicated = M_CheckParm("-dedicated") != 0; | ||||
| 	connectedtodedicated = dedicated; | ||||
| 	if (dedicated) | ||||
| 	{ | ||||
| 		usedTourney = true; | ||||
|  |  | |||
|  | @ -3015,9 +3015,19 @@ static void Got_Mapcmd(const UINT8 **cp, INT32 playernum) | |||
| 		CON_LogMessage(M_GetText("Speeding off to level...\n")); | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	if (demo.playback && !demo.timing) | ||||
| 		precache = false; | ||||
| 
 | ||||
| 
 | ||||
| 	// Save demo in case map change happened after level finish
 | ||||
| 	// (either manually with the map command, or with a redo vote)
 | ||||
| 	// Isn't needed for time attack (and would also cause issues, as there
 | ||||
| 	// G_RecordDemo (which sets demo.recording to true) is called before this runs)
 | ||||
| 	if (demo.recording && modeattacking == ATTACKING_NONE) | ||||
| 		G_CheckDemoStatus(); | ||||
| 
 | ||||
| 
 | ||||
| 	demo.willsave = (cv_recordmultiplayerdemos.value == 2); | ||||
| 	demo.savebutton = 0; | ||||
| 
 | ||||
|  | @ -4474,7 +4484,7 @@ static void Command_Addfile(void) | |||
| 		} | ||||
| 
 | ||||
| 		// Add file on your client directly if it is trivial, or you aren't in a netgame.
 | ||||
| 		if (!(netgame || multiplayer) || musiconly) | ||||
| 		if (!netgame || musiconly) | ||||
| 		{ | ||||
| 			P_AddWadFile(fn); | ||||
| 			addedfiles[numfilesadded++] = fn; | ||||
|  | @ -5719,6 +5729,11 @@ static void Got_SetupVotecmd(const UINT8 **cp, INT32 playernum) | |||
| 
 | ||||
| 	memcpy(g_voteLevels, tempVoteLevels, sizeof(g_voteLevels)); | ||||
| 
 | ||||
| 	// admin can force vote state whenever
 | ||||
| 	// so we have to save this replay if it needs to be saved
 | ||||
| 	if (demo.recording) | ||||
| 		G_CheckDemoStatus(); | ||||
| 
 | ||||
| 	G_SetGamestate(GS_VOTING); | ||||
| 	Y_StartVote(); | ||||
| } | ||||
|  | @ -6090,7 +6105,7 @@ static void Got_Cheat(const UINT8 **cp, INT32 playernum) | |||
| 			K_StopRoulette(&player->itemRoulette); | ||||
| 
 | ||||
| 			player->itemtype = item; | ||||
| 			player->itemamount = amt; | ||||
| 			K_SetPlayerItemAmount(player, amt); | ||||
| 
 | ||||
| 			if (amt == 0) | ||||
| 			{ | ||||
|  |  | |||
|  | @ -738,6 +738,7 @@ struct player_t | |||
| 	UINT8 position;			// Used for Kart positions, mostly for deterministic stuff
 | ||||
| 	UINT8 oldposition;		// Used for taunting when you pass someone
 | ||||
| 	UINT8 positiondelay;	// Used for position number, so it can grow when passing
 | ||||
| 	UINT8 leaderpenalty;	// Used for penalising 1st in a positiondelay-friendly way
 | ||||
| 
 | ||||
| 	UINT8 teamposition;		// Position, but only against other teams -- not your own.
 | ||||
| 	UINT8 teamimportance;	// Opposite of team position x2, with +1 for being in 1st.
 | ||||
|  | @ -769,6 +770,7 @@ struct player_t | |||
| 	UINT8 wipeoutslow;		// Timer before you slowdown when getting wiped out
 | ||||
| 	UINT8 justbumped;		// Prevent players from endlessly bumping into each other
 | ||||
| 	UINT8 noEbrakeMagnet;	// Briefly disable 2.2 responsive ebrake if you're bumped by another player.
 | ||||
| 	UINT8 wallSpikeDampen;	// 2.4 wallspikes can softlock in closed quarters... attenuate their violence
 | ||||
| 	UINT8 tumbleBounces; | ||||
| 	UINT16 tumbleHeight;	// In *mobjscaled* fracunits, or mfu, not raw fu
 | ||||
| 	UINT16 stunned;			// Number of tics during which rings cannot be picked up
 | ||||
|  |  | |||
|  | @ -220,6 +220,7 @@ extern boolean imcontinuing; // Temporary flag while continuing | |||
| #define ATTACKING_LAP	(1<<1) | ||||
| #define ATTACKING_SPB	(1<<2) | ||||
| extern UINT8 modeattacking; | ||||
| const char *M_GetRecordMode(void); | ||||
| 
 | ||||
| // menu demo things
 | ||||
| extern UINT8  numDemos; | ||||
|  |  | |||
|  | @ -239,6 +239,7 @@ enum {false = 0, true = 1}; | |||
| 	#endif | ||||
| 
 | ||||
| 	#define ATTRUNUSED __attribute__((unused)) | ||||
| 	#define ATTRUNOPTIMIZE __attribute__((optimize("O0"))) | ||||
| #elif defined (_MSC_VER) | ||||
| 	#define ATTRNORETURN __declspec(noreturn) | ||||
| 	#define ATTRINLINE __forceinline | ||||
|  |  | |||
|  | @ -3517,15 +3517,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) | |||
| 	} | ||||
| 
 | ||||
| 	// end of player read (the 0xFF marker)
 | ||||
| 	// so this is where we are to read our lua variables (if possible!)
 | ||||
| 	if (demoflags & DF_LUAVARS)	// again, used for compability, lua shit will be saved to replays regardless of if it's even been loaded
 | ||||
| 	{ | ||||
| 		if (!gL) // No Lua state! ...I guess we'll just start one...
 | ||||
| 			LUA_ClearState(); | ||||
| 
 | ||||
| 		// No modeattacking check, DF_LUAVARS won't be present here.
 | ||||
| 		LUA_UnArchive(&demobuf, false); | ||||
| 	} | ||||
| 	// see the DF_LUAVARS if later, though.
 | ||||
| 
 | ||||
| 	splitscreen = 0; | ||||
| 
 | ||||
|  | @ -3547,6 +3539,18 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) | |||
| 
 | ||||
| 	G_InitNew((demoflags & DF_ENCORE) != 0, gamemap, true, true); // Doesn't matter whether you reset or not here, given changes to resetplayer.
 | ||||
| 
 | ||||
| 	// so this is where we are to read our lua variables (if possible!)
 | ||||
| 	// we read it here because Lua player variables can have mobj references,
 | ||||
| 	// and not having the map loaded causes crashes if that's the case.
 | ||||
| 	if (demoflags & DF_LUAVARS)	// again, used for compability, lua shit will be saved to replays regardless of if it's even been loaded
 | ||||
| 	{ | ||||
| 		if (!gL) // No Lua state! ...I guess we'll just start one...
 | ||||
| 			LUA_ClearState(); | ||||
| 
 | ||||
| 		// No modeattacking check, DF_LUAVARS won't be present here.
 | ||||
| 		LUA_UnArchive(&demobuf, false); | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < numslots; i++) | ||||
| 	{ | ||||
| 		UINT8 j; | ||||
|  | @ -4212,6 +4216,7 @@ boolean G_CheckDemoStatus(void) | |||
| 
 | ||||
| 	if (modeattacking || demo.willsave) | ||||
| 	{ | ||||
| 		demo.willsave = false; | ||||
| 		if (demobuf.p) | ||||
| 		{ | ||||
| 			G_SaveDemo(); | ||||
|  |  | |||
							
								
								
									
										99
									
								
								src/g_game.c
									
										
									
									
									
								
							
							
						
						
									
										99
									
								
								src/g_game.c
									
										
									
									
									
								
							|  | @ -496,7 +496,11 @@ bademblem: | |||
| 
 | ||||
| 	if (!gonnadrawtime && showownrecord) | ||||
| 	{ | ||||
| 		stickermedalinfo.timetoreach = G_GetBestTime(map); | ||||
| 		stickermedalinfo.timetoreach = (encoremode == true) | ||||
| 			? mapheaderinfo[map]->records.spbattack.time | ||||
| 			: mapheaderinfo[map]->records.timeattack.time; | ||||
| 		if (!stickermedalinfo.timetoreach) | ||||
| 			stickermedalinfo.timetoreach = UINT32_MAX; | ||||
| 	} | ||||
| 
 | ||||
| 	if (stickermedalinfo.timetoreach != UINT32_MAX) | ||||
|  | @ -584,6 +588,7 @@ static void G_UpdateRecordReplays(void) | |||
| 	char lastdemo[256], bestdemo[256]; | ||||
| 	const char *modeprefix = ""; | ||||
| 
 | ||||
| 	// See also M_GetRecordMode
 | ||||
| 	if (encoremode) | ||||
| 	{ | ||||
| 		modeprefix = "spb-"; | ||||
|  | @ -1413,6 +1418,7 @@ boolean G_Responder(event_t *ev) | |||
| 		if (HU_Responder(ev)) | ||||
| 		{ | ||||
| 			hu_keystrokes = true; | ||||
| 			chat_keydown = true; | ||||
| 			return true; // chat ate the event
 | ||||
| 		} | ||||
| 	} | ||||
|  | @ -1522,6 +1528,7 @@ boolean G_Responder(event_t *ev) | |||
| 			return true; | ||||
| 
 | ||||
| 		case ev_keyup: | ||||
| 			chat_keydown = false; // prevents repeat inputs from inputs made with chat open
 | ||||
| 			return false; // always let key up events filter down
 | ||||
| 
 | ||||
| 		case ev_mouse: | ||||
|  | @ -2685,7 +2692,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) | |||
| 
 | ||||
| 	// SRB2kart
 | ||||
| 	p->itemtype = itemtype; | ||||
| 	p->itemamount = itemamount; | ||||
| 	K_SetPlayerItemAmount(p, itemamount); | ||||
| 	p->growshrinktimer = growshrinktimer; | ||||
| 	p->karmadelay = 0; | ||||
| 	p->eggmanblame = -1; | ||||
|  | @ -4064,12 +4071,91 @@ UINT16 G_RandMap(UINT32 tolflags, UINT16 pprevmap, boolean ignoreBuffers, boolea | |||
| 
 | ||||
| void G_AddMapToBuffer(UINT16 map) | ||||
| { | ||||
| #if 0 | ||||
| 	// DEBUG: make nearly everything but four race levels full justPlayed
 | ||||
| 	// to look into what happens when a dedicated runs for seven million years.
 | ||||
| 	INT32 justplayedvalue = TOLMaps(gametype) - VOTE_NUM_LEVELS; | ||||
| 	UINT32 tolflag = G_TOLFlag(gametype); | ||||
| 
 | ||||
| 	// Find all the maps that are ok
 | ||||
| 	INT32 i; | ||||
| 	for (i = 0; i < nummapheaders; i++) | ||||
| 	{ | ||||
| 		if (mapheaderinfo[i] == NULL) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (mapheaderinfo[i]->lumpnum == LUMPERROR) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if ((mapheaderinfo[i]->typeoflevel & tolflag) == 0) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (mapheaderinfo[i]->menuflags & LF2_HIDEINMENU) | ||||
| 		{ | ||||
| 			// Don't include hidden
 | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		// Only care about restrictions if the host is a listen server.
 | ||||
| 		if (!dedicated) | ||||
| 		{ | ||||
| 			if (!(mapheaderinfo[i]->menuflags & LF2_NOVISITNEEDED) | ||||
| 			&& !(mapheaderinfo[i]->records.mapvisited & MV_VISITED) | ||||
| 			&& !( | ||||
| 				mapheaderinfo[i]->cup | ||||
| 				&& mapheaderinfo[i]->cup->cachedlevels[0] == i | ||||
| 			)) | ||||
| 			{ | ||||
| 				// Not visited OR head of cup
 | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			if ((mapheaderinfo[i]->menuflags & LF2_FINISHNEEDED) | ||||
| 			&& !(mapheaderinfo[i]->records.mapvisited & MV_BEATEN)) | ||||
| 			{ | ||||
| 				// Not completed
 | ||||
| 				continue; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (M_MapLocked(i + 1) == true) | ||||
| 		{ | ||||
| 			// We haven't earned this one.
 | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		mapheaderinfo[i]->justPlayed = justplayedvalue; | ||||
| 		justplayedvalue -= 1; | ||||
| 		if (justplayedvalue <= 0) | ||||
| 			break; | ||||
| 	} | ||||
| #else | ||||
| 	if (dedicated && D_NumPlayers() == 0) | ||||
| 		return; | ||||
| 
 | ||||
| 	const size_t upperJustPlayedLimit = TOLMaps(gametype) - VOTE_NUM_LEVELS - 1; | ||||
| 
 | ||||
| 	if (mapheaderinfo[map]->justPlayed == 0) // Started playing a new map.
 | ||||
| 	{ | ||||
| 		// Decrement every maps' justPlayed value.
 | ||||
| 		INT32 i; | ||||
| 		for (i = 0; i < nummapheaders; i++) | ||||
| 		{ | ||||
| 			// If the map's justPlayed value is higher
 | ||||
| 			// than what it should be, clamp it.
 | ||||
| 			// (Usually a result of SOC files
 | ||||
| 			// manipulating which levels are hidden.)
 | ||||
| 			if (mapheaderinfo[i]->justPlayed > upperJustPlayedLimit) | ||||
| 			{ | ||||
| 				mapheaderinfo[i]->justPlayed = upperJustPlayedLimit; | ||||
| 			} | ||||
| 			 | ||||
| 			if (mapheaderinfo[i]->justPlayed > 0) | ||||
| 			{ | ||||
| 				mapheaderinfo[i]->justPlayed--; | ||||
|  | @ -4078,8 +4164,9 @@ void G_AddMapToBuffer(UINT16 map) | |||
| 	} | ||||
| 
 | ||||
| 	// Set our map's justPlayed value.
 | ||||
| 	mapheaderinfo[map]->justPlayed = TOLMaps(gametype) - VOTE_NUM_LEVELS; | ||||
| 	mapheaderinfo[map]->justPlayed = upperJustPlayedLimit; | ||||
| 	mapheaderinfo[map]->anger = 0; // Reset voting anger now that we're playing it
 | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| //
 | ||||
|  | @ -4997,7 +5084,10 @@ void G_AfterIntermission(void) | |||
| 		return; | ||||
| 	} | ||||
| 	else if (demo.recording && (modeattacking || demo.willsave)) | ||||
| 	{ | ||||
| 		demo.willsave = false; | ||||
| 		G_SaveDemo(); | ||||
| 	} | ||||
| 	else if (demo.recording) | ||||
| 		G_ResetDemoRecording(); | ||||
| 
 | ||||
|  | @ -5135,6 +5225,9 @@ static void G_DoContinued(void) | |||
| // when something new is added.
 | ||||
| void G_EndGame(void) | ||||
| { | ||||
| 	// Clean up ACS music remaps.
 | ||||
| 	Music_TuneReset(); | ||||
| 	 | ||||
| 	// Handle voting
 | ||||
| 	if (nextmap == NEXTMAP_VOTING) | ||||
| 	{ | ||||
|  |  | |||
|  | @ -81,6 +81,8 @@ extern struct menuqueue | |||
| 	UINT8 size; | ||||
| 	UINT8 sending; | ||||
| 	UINT8 anchor; | ||||
| 	boolean clearing; | ||||
| 	boolean cupqueue; | ||||
| 	roundentry_t entries[ROUNDQUEUE_MAX]; | ||||
| } menuqueue; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1386,8 +1386,18 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom | |||
| 
 | ||||
| 				grTex = HWR_GetTexture(gl_midtexture, gl_sidedef->midtexture); | ||||
| 
 | ||||
| 				wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY; | ||||
| 				wallVerts[0].t = wallVerts[1].t = (h - l + texturevpeg) * grTex->scaleY; | ||||
| 				// Check if we should flip tripwire texture vertically for unpegged tripwires
 | ||||
| 				if (R_ShouldFlipTripWire(gl_linedef)) | ||||
| 				{ | ||||
| 					// Flip texture coordinates vertically
 | ||||
| 					wallVerts[0].t = wallVerts[1].t = texturevpeg * grTex->scaleY; | ||||
| 					wallVerts[3].t = wallVerts[2].t = (h - l + texturevpeg) * grTex->scaleY; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY; | ||||
| 					wallVerts[0].t = wallVerts[1].t = (h - l + texturevpeg) * grTex->scaleY; | ||||
| 				} | ||||
| 				wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX; | ||||
| 				wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX; | ||||
| 			} | ||||
|  | @ -1433,8 +1443,19 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom | |||
| 						texturevpeg = textureheight[gl_sidedef->midtexture]*repeats - h + polybottom; | ||||
| 					else | ||||
| 						texturevpeg = polytop - h; | ||||
| 					wallVerts[2].t = texturevpeg * grTex->scaleY; | ||||
| 					wallVerts[1].t = (h - l + texturevpeg) * grTex->scaleY; | ||||
| 					 | ||||
| 					// Apply tripwire flipping for slope correction as well
 | ||||
| 					if (R_ShouldFlipTripWire(gl_linedef)) | ||||
| 					{ | ||||
| 						// Flip texture coordinates vertically
 | ||||
| 						wallVerts[1].t = texturevpeg * grTex->scaleY; | ||||
| 						wallVerts[2].t = (h - l + texturevpeg) * grTex->scaleY; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						wallVerts[2].t = texturevpeg * grTex->scaleY; | ||||
| 						wallVerts[1].t = (h - l + texturevpeg) * grTex->scaleY; | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				wallVerts[2].y = FIXED_TO_FLOAT(h); | ||||
|  | @ -1531,8 +1552,18 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom | |||
| 
 | ||||
| 				grTex = HWR_GetTexture(gl_midtexture, gl_sidedef->midtexture); | ||||
| 
 | ||||
| 				wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY; | ||||
| 				wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_frontsector->ceilingheight - gl_frontsector->floorheight) * grTex->scaleY; | ||||
| 				// Check if we should flip tripwire texture vertically for single-sided lines too
 | ||||
| 				if (R_ShouldFlipTripWire(gl_linedef)) | ||||
| 				{ | ||||
| 					// Flip texture coordinates vertically
 | ||||
| 					wallVerts[0].t = wallVerts[1].t = texturevpeg * grTex->scaleY; | ||||
| 					wallVerts[3].t = wallVerts[2].t = (texturevpeg + gl_frontsector->ceilingheight - gl_frontsector->floorheight) * grTex->scaleY; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY; | ||||
| 					wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_frontsector->ceilingheight - gl_frontsector->floorheight) * grTex->scaleY; | ||||
| 				} | ||||
| 				wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX; | ||||
| 				wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX; | ||||
| 
 | ||||
|  |  | |||
|  | @ -85,6 +85,7 @@ patch_t *frameslash;	// framerate stuff. Used in screen.c | |||
| 
 | ||||
| static player_t *plr; | ||||
| boolean hu_keystrokes; // :)
 | ||||
| boolean chat_keydown; | ||||
| boolean chat_on; // entering a chat message?
 | ||||
| boolean g_voicepushtotalk_on; // holding PTT?
 | ||||
| static char w_chat[HU_MAXMSGLEN + 1]; | ||||
|  |  | |||
|  | @ -124,6 +124,9 @@ typedef enum | |||
| // some functions
 | ||||
| void HU_AddChatText(const char *text, boolean playsound); | ||||
| 
 | ||||
| // set true when key is pressed while chat is open
 | ||||
| extern boolean chat_keydown; | ||||
| 
 | ||||
| // set true when entering a chat message
 | ||||
| extern boolean chat_on; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1229,6 +1229,7 @@ boolean I_InitTcpNetwork(void) | |||
| 	if (M_CheckParm("-server") || dedicated) | ||||
| 	{ | ||||
| 		server = true; | ||||
| 		connectedtodedicated = dedicated; | ||||
| 
 | ||||
| 		// If a number of clients (i.e. nodes) is specified, the server will wait for the clients
 | ||||
| 		// to connect before starting.
 | ||||
|  |  | |||
							
								
								
									
										70
									
								
								src/info.c
									
										
									
									
									
								
							
							
						
						
									
										70
									
								
								src/info.c
									
										
									
									
									
								
							|  | @ -13780,7 +13780,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -13807,7 +13807,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -13915,7 +13915,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -13942,7 +13942,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -13969,7 +13969,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -13996,7 +13996,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14023,7 +14023,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14050,7 +14050,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14077,7 +14077,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14104,7 +14104,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14131,7 +14131,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14158,7 +14158,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14185,7 +14185,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14212,7 +14212,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14239,7 +14239,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14266,7 +14266,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17561,7 +17561,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,              // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_SPECIAL, // flags
 | ||||
| 		MF_NOGRAVITY|MF_SPECIAL|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17588,7 +17588,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,              // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_SPECIAL, // flags
 | ||||
| 		MF_NOGRAVITY|MF_SPECIAL|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17615,7 +17615,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,           // mass
 | ||||
| 		0,           // damage
 | ||||
| 		sfx_supert,  // activesound
 | ||||
| 		MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
 | ||||
| 		MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL       // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17642,7 +17642,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,           // mass
 | ||||
| 		0,           // damage
 | ||||
| 		sfx_None,    // activesound
 | ||||
| 		MF_NOCLIPHEIGHT|MF_SPECIAL|MF_NOGRAVITY, // flags
 | ||||
| 		MF_NOCLIPHEIGHT|MF_SPECIAL|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL       // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17669,7 +17669,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,           // mass
 | ||||
| 		0,           // damage
 | ||||
| 		sfx_None,    // activesound
 | ||||
| 		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
 | ||||
| 		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL       // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17858,7 +17858,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,              // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SPECIAL|MF_SHOOTABLE, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SPECIAL|MF_SHOOTABLE|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19020,7 +19020,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 	    0,              // damage
 | ||||
| 	    sfx_None,       // activesound
 | ||||
| 
 | ||||
| 	    MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags
 | ||||
| 	    MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL,			// raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19048,7 +19048,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 	    0,              // damage
 | ||||
| 	    sfx_None,       // activesound
 | ||||
| 
 | ||||
| 	    MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags
 | ||||
| 	    MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL,			// raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19076,7 +19076,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 	    0,              // damage
 | ||||
| 	    sfx_None,       // activesound
 | ||||
| 
 | ||||
| 	    MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags
 | ||||
| 	    MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL,			// raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19481,7 +19481,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,              // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTPUNT, // flags
 | ||||
| 		MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTPUNT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19508,7 +19508,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,              // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
 | ||||
| 		MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19643,7 +19643,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,              // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
 | ||||
| 		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19697,7 +19697,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,              // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY, // flags
 | ||||
| 		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19724,7 +19724,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,              // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY, // flags
 | ||||
| 		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -20129,7 +20129,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,              // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY, // flags
 | ||||
| 		MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -20156,7 +20156,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,              // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY, // flags
 | ||||
| 		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -22764,7 +22764,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,            // mass
 | ||||
| 		0,            // damage
 | ||||
| 		sfx_None,     // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL        // raisestate
 | ||||
| 	}, | ||||
| 	{           // MT_EXP
 | ||||
|  | @ -22790,7 +22790,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,            // mass
 | ||||
| 		0,            // damage
 | ||||
| 		sfx_None,     // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING, // flags
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL        // raisestate
 | ||||
| 	}, | ||||
| 	{           // MT_FLYBOT767
 | ||||
|  | @ -22816,7 +22816,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = | |||
| 		0,            // mass
 | ||||
| 		0,            // damage
 | ||||
| 		sfx_None,     // activesound
 | ||||
| 		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING, // flags
 | ||||
| 		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL        // raisestate
 | ||||
| 	}, | ||||
| 	{           // MT_STONESHOE
 | ||||
|  |  | |||
|  | @ -740,8 +740,11 @@ static void K_SpawnOvertimeLaser(fixed_t x, fixed_t y, fixed_t scale) | |||
| 				case 0: | ||||
| 					P_SetMobjState(mo, S_OVERTIME_BULB1); | ||||
| 
 | ||||
| 					if (leveltime & 1) | ||||
| 						mo->frame += 1; | ||||
| 					if (!cv_reducevfx.value) | ||||
| 					{ | ||||
| 						if (leveltime & 1) | ||||
| 							mo->frame += 1; | ||||
| 					} | ||||
| 
 | ||||
| 					//P_SetScale(mo, mapobjectscale);
 | ||||
| 					zpos += 35 * mo->scale * flip; | ||||
|  | @ -749,10 +752,13 @@ static void K_SpawnOvertimeLaser(fixed_t x, fixed_t y, fixed_t scale) | |||
| 				case 1: | ||||
| 					P_SetMobjState(mo, S_OVERTIME_LASER); | ||||
| 
 | ||||
| 					if (leveltime & 1) | ||||
| 						mo->frame += 3; | ||||
| 					else | ||||
| 						mo->frame += (leveltime / 2) % 3; | ||||
| 					if (!cv_reducevfx.value) | ||||
| 					{ | ||||
| 						if (leveltime & 1) | ||||
| 							mo->frame += 3; | ||||
| 						else | ||||
| 							mo->frame += (leveltime / 2) % 3; | ||||
| 					} | ||||
| 
 | ||||
| 					//P_SetScale(mo, scale);
 | ||||
| 					zpos += 346 * mo->scale * flip; | ||||
|  | @ -763,8 +769,11 @@ static void K_SpawnOvertimeLaser(fixed_t x, fixed_t y, fixed_t scale) | |||
| 				case 2: | ||||
| 					P_SetMobjState(mo, S_OVERTIME_BULB2); | ||||
| 
 | ||||
| 					if (leveltime & 1) | ||||
| 						mo->frame += 1; | ||||
| 					if (!cv_reducevfx.value) | ||||
| 					{ | ||||
| 						if (leveltime & 1) | ||||
| 							mo->frame += 1; | ||||
| 					} | ||||
| 
 | ||||
| 					//P_SetScale(mo, mapobjectscale);
 | ||||
| 					break; | ||||
|  | @ -1029,6 +1038,8 @@ boolean K_EndBattleRound(player_t *victor) | |||
| 			// exiting, the round has already ended.
 | ||||
| 			return false; | ||||
| 		} | ||||
| 		 | ||||
| 		UINT32 topscore = 0; | ||||
| 
 | ||||
| 		if (gametyperules & GTR_POINTLIMIT) | ||||
| 		{ | ||||
|  | @ -1037,7 +1048,27 @@ boolean K_EndBattleRound(player_t *victor) | |||
| 			// TODO: a "won the round" bool used for sorting
 | ||||
| 			// position / intermission, so we aren't completely
 | ||||
| 			// clobbering the individual scoring.
 | ||||
| 			victor->roundscore = 100; | ||||
| 			 | ||||
| 			// This isn't quite the above TODO but it's something?
 | ||||
| 			// For purposes of score-to-EXP conversion, we need to not lock the winner to an arbitrarily high score.
 | ||||
| 			// Instead, let's find the highest score, and if they're not the highest scoring player,
 | ||||
| 			// give them a bump so they *are* the highest scoring player.
 | ||||
| 			for (INT32 i = 0; i < MAXPLAYERS; i++) | ||||
| 			{ | ||||
| 				if (!playeringame[i] || players[i].spectator) | ||||
| 				{ | ||||
| 					continue; | ||||
| 				} | ||||
| 				 | ||||
| 				if ((&players[i])->roundscore > topscore) | ||||
| 				{ | ||||
| 					topscore = (&players[i])->roundscore; | ||||
| 				} | ||||
| 			} | ||||
| 			if (victor->roundscore <= topscore) | ||||
| 			{ | ||||
| 				victor->roundscore = topscore + 3; | ||||
| 			} | ||||
| 
 | ||||
| 			if (G_GametypeHasTeams() == true && victor->team != TEAM_UNASSIGNED) | ||||
| 			{ | ||||
|  |  | |||
|  | @ -227,7 +227,7 @@ void K_UpdateMatchRaceBots(void) | |||
| { | ||||
| 	const UINT16 defaultbotskin = R_BotDefaultSkin(); | ||||
| 	UINT8 difficulty; | ||||
| 	UINT8 pmax = (dedicated ? MAXPLAYERS-1 : MAXPLAYERS); | ||||
| 	UINT8 pmax = (InADedicatedServer() ? MAXPLAYERS-1 : MAXPLAYERS); | ||||
| 	UINT8 numplayers = 0; | ||||
| 	UINT8 numbots = 0; | ||||
| 	UINT8 numwaiting = 0; | ||||
|  | @ -343,12 +343,7 @@ void K_UpdateMatchRaceBots(void) | |||
| 	if (numbots < wantedbots) | ||||
| 	{ | ||||
| 		// We require MORE bots!
 | ||||
| 		UINT8 newplayernum = 0; | ||||
| 
 | ||||
| 		if (dedicated) | ||||
| 		{ | ||||
| 			newplayernum = 1; | ||||
| 		} | ||||
| 		UINT8 newplayernum = InADedicatedServer() ? 1 : 0; | ||||
| 
 | ||||
| 		// Rearrange usable bot skins list to prevent gaps for randomised selection
 | ||||
| 		if (tutorialchallenge == TUTORIALSKIP_INPROGRESS) | ||||
|  |  | |||
|  | @ -721,6 +721,11 @@ boolean K_DropTargetCollide(mobj_t *t1, mobj_t *t2) | |||
| 		t2->angle += ANGLE_180; | ||||
| 		if (t2->type == MT_JAWZ) | ||||
| 			P_SetTarget(&t2->tracer, t2->target); // Back to the source!
 | ||||
| 		 | ||||
| 		// Reflected item becomes owned by the DT owner, so it becomes dangerous the the thrower
 | ||||
| 		if (t1->target && !P_MobjWasRemoved(t1->target)) | ||||
| 			P_SetTarget(&t2->target, t1->target); | ||||
| 		 | ||||
| 		t2->threshold = 10; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -6865,7 +6865,13 @@ static void K_drawKartStartCountdown(void) | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if ((leveltime % (2*5)) / 5) // blink
 | ||||
| 		int flashrate = 5; | ||||
| 		if (cv_reducevfx.value) | ||||
| 		{ | ||||
| 			flashrate = 35; | ||||
| 		} | ||||
| 
 | ||||
| 		if ((leveltime % (2*flashrate)) / flashrate) // blink
 | ||||
| 			pnum += 5; | ||||
| 		if (r_splitscreen) // splitscreen
 | ||||
| 			pnum += 10; | ||||
|  |  | |||
							
								
								
									
										318
									
								
								src/k_kart.c
									
										
									
									
									
								
							
							
						
						
									
										318
									
								
								src/k_kart.c
									
										
									
									
									
								
							|  | @ -72,6 +72,38 @@ | |||
| // comeback is Battle Mode's karma comeback, also bool
 | ||||
| // mapreset is set when enough players fill an empty server
 | ||||
| 
 | ||||
| UINT8 K_SetPlayerItemAmount(player_t *player, INT32 amount) | ||||
| { | ||||
| 	if (amount & ~UINT8_MAX) | ||||
| 	{ | ||||
| 		// having bits outside of valid range means time to cap
 | ||||
| 		amount = (amount < 0) ? 0 : UINT8_MAX; | ||||
| 	} | ||||
| 
 | ||||
| 	return (player->itemamount = amount); | ||||
| } | ||||
| 
 | ||||
| UINT8 K_SetPlayerBackupItemAmount(player_t *player, INT32 amount) | ||||
| { | ||||
| 	if (amount & ~UINT8_MAX) | ||||
| 	{ | ||||
| 		// having bits outside of valid range means time to cap
 | ||||
| 		amount = (amount < 0) ? 0 : UINT8_MAX; | ||||
| 	} | ||||
| 
 | ||||
| 	return (player->backupitemamount = amount); | ||||
| } | ||||
| 
 | ||||
| UINT8 K_AdjustPlayerItemAmount(player_t *player, INT32 amount) | ||||
| { | ||||
| 	return K_SetPlayerItemAmount(player, player->itemamount + amount); | ||||
| } | ||||
| 
 | ||||
| UINT8 K_AdjustPlayerBackupItemAmount(player_t *player, INT32 amount) | ||||
| { | ||||
| 	return K_SetPlayerBackupItemAmount(player, player->backupitemamount + amount); | ||||
| } | ||||
| 
 | ||||
| void K_PopBubbleShield(player_t *player) | ||||
| { | ||||
| 	if (player->curshield != KSHIELD_BUBBLE) | ||||
|  | @ -81,7 +113,7 @@ void K_PopBubbleShield(player_t *player) | |||
| 
 | ||||
| 	player->curshield = KSHIELD_NONE; | ||||
| 	player->itemtype = 0; | ||||
| 	player->itemamount = 0; | ||||
| 	K_SetPlayerItemAmount(player, 0); | ||||
| 	player->itemflags &= ~(IF_ITEMOUT|IF_EGGMANOUT); | ||||
| 
 | ||||
| 	K_AddHitLag(player->mo, 4, false); | ||||
|  | @ -1243,12 +1275,50 @@ boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj) | |||
| 
 | ||||
| 	if (solidMobj->type == MT_WALLSPIKE) | ||||
| 	{ | ||||
| 		if (bounceMobj->player && bounceMobj->hitlag) | ||||
| 		{ | ||||
| 			bounceMobj->player->justbumped = bumptime; | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		//CONS_Printf("%sattenuation is %d\n", (leveltime & 1 ? "" : " "), bounceMobj->player->wallSpikeDampen);
 | ||||
| 
 | ||||
| 		// Always thrust out towards the tip
 | ||||
| 		// (...don't try to roll our own bad calculations,
 | ||||
| 		// just make this behave like a wallspring...)
 | ||||
| 
 | ||||
| 		P_DoSpringEx(bounceMobj, mapobjectscale, 0, solidMobj->info->damage, | ||||
| 			solidMobj->angle, SKINCOLOR_NONE); | ||||
| 		fixed_t spikeforce = solidMobj->info->damage; | ||||
| 		fixed_t deflection = 0; | ||||
| 
 | ||||
| 		if (bounceMobj->player && !G_CompatLevel(0x0011)) | ||||
| 		{ | ||||
| 			// Okay no we need to use bad calculations just to
 | ||||
| 			// prevent softlocks -- repeated touches attenuate
 | ||||
| 			UINT8 atten = bounceMobj->player->wallSpikeDampen; | ||||
| 
 | ||||
| 			deflection = atten * FRACUNIT; | ||||
| 			if (bounceMobj->angle - solidMobj->angle >= ANGLE_180) | ||||
| 				deflection = -deflection; | ||||
| 
 | ||||
| 			K_StumblePlayer(bounceMobj->player); | ||||
| 			bounceMobj->player->tumbleBounces = TUMBLEBOUNCES; // Only one
 | ||||
| 
 | ||||
| 			atten++; | ||||
| 			while (atten) | ||||
| 			{ | ||||
| 				// We want a power relationship - towards zero but not quite reaching it.
 | ||||
| 				spikeforce = (2 * spikeforce) / 3; | ||||
| 				atten--; | ||||
| 			} | ||||
| 
 | ||||
| 			if (bounceMobj->player->wallSpikeDampen < UINT8_MAX | ||||
| 			&& bounceMobj->player->justbumped < bumptime-2) | ||||
| 				bounceMobj->player->wallSpikeDampen++; | ||||
| 		} | ||||
| 
 | ||||
| 		P_DoSpringEx(bounceMobj, mapobjectscale, 0, spikeforce, | ||||
| 			solidMobj->angle + R_PointToAngle2(0, 0, spikeforce, deflection), | ||||
| 			SKINCOLOR_NONE); | ||||
| 
 | ||||
| 		K_PlayerJustBumped(bounceMobj->player); | ||||
| 
 | ||||
|  | @ -3931,7 +4001,7 @@ static void K_GetKartBoostPower(player_t *player) | |||
| 
 | ||||
| 	if (player->eggmanexplode) // Ready-to-explode
 | ||||
| 	{ | ||||
| 		ADDBOOST(6*FRACUNIT/20, FRACUNIT, 0); // + 30% top speed, + 100% acceleration, +0% handling
 | ||||
| 		ADDBOOST(9*FRACUNIT/20, FRACUNIT, 0); // + 45% top speed, + 100% acceleration, +0% handling
 | ||||
| 	} | ||||
| 
 | ||||
| 	if (player->vortexBoost) // Holding wavedash vortex (assigned in K_UpdateWavedashIndicator!)
 | ||||
|  | @ -4007,7 +4077,12 @@ static void K_GetKartBoostPower(player_t *player) | |||
| 	// This should always remain the last boost stack before tethering
 | ||||
| 	if (player->botvars.rubberband > FRACUNIT && K_PlayerUsesBotMovement(player) == true) | ||||
| 	{ | ||||
| 		ADDBOOST(player->botvars.rubberband - FRACUNIT, 0, 0); | ||||
| 		fixed_t rubber = player->botvars.rubberband - FRACUNIT; | ||||
| 
 | ||||
| 		if (!G_CompatLevel(0x0011)) | ||||
| 			rubber = FixedRescale(player->botvars.recentDeflection, 0, BOTMAXDEFLECTION, Easing_Linear, rubber, 8*rubber/10); | ||||
| 
 | ||||
| 		ADDBOOST(rubber, 0, 0); | ||||
| 	} | ||||
| 
 | ||||
| 	if (player->draftpower > 0) // Drafting
 | ||||
|  | @ -4500,6 +4575,9 @@ boolean K_PvPAmpReward(UINT32 award, player_t *attacker, player_t *defender) | |||
| 	if (!K_PlayerUsesBotMovement(attacker) && K_PlayerUsesBotMovement(defender)) | ||||
| 		award /= 2; | ||||
| 
 | ||||
| 	if (award == 0) | ||||
| 		award = 1; | ||||
| 
 | ||||
| 	return award; | ||||
| } | ||||
| 
 | ||||
|  | @ -4516,7 +4594,13 @@ void K_SpawnAmps(player_t *player, UINT8 amps, mobj_t *impact) | |||
| 
 | ||||
| 	UINT32 itemdistance = min(FRACUNIT-1, K_GetItemRouletteDistance(player, D_NumPlayersInRace())); // cap this to FRACUNIT-1, so it doesn't wrap when turning it into fixed_t
 | ||||
| 	fixed_t itemdistmult = FRACUNIT + min(FRACUNIT, (itemdistance<<FRACBITS) / MAXAMPSCALINGDIST); | ||||
| 	UINT16 scaledamps = min(amps, amps * (10 + (9-player->kartspeed) - (9-player->kartweight)) / 10); | ||||
| 
 | ||||
| 	INT32 weighting = player->kartweight - player->kartspeed; | ||||
| 	INT32 minweight = 1 - 9; | ||||
| 	INT32 maxweight = 9 - 1; | ||||
| 
 | ||||
| 	UINT16 scaledamps = FixedRescale(weighting, minweight, maxweight, Easing_Linear, amps/2, 5*amps/4); | ||||
| 
 | ||||
| 	// Debug print for scaledamps calculation
 | ||||
| 	// CONS_Printf("K_SpawnAmps: player=%s, amps=%d, kartspeed=%d, kartweight=%d, itemdistance=%d, itemdistmult=%0.2f, statscaledamps=%d, distscaledamps=%d\n",
 | ||||
| 	// 	player_names[player-players], amps, player->kartspeed, player->kartweight,
 | ||||
|  | @ -4939,7 +5023,6 @@ boolean K_Overdrive(player_t *player) | |||
| 
 | ||||
| 	player->amps = 0; | ||||
| 	player->overdriveready = 0; | ||||
| 	player->overdrivelenient = false; | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
|  | @ -6772,6 +6855,14 @@ static void K_SpawnDriftSparks(player_t *player) | |||
| 	I_Assert(player->mo != NULL); | ||||
| 	I_Assert(!P_MobjWasRemoved(player->mo)); | ||||
| 
 | ||||
| 	if (player->driftcharge >= dsthree) | ||||
| 	{ | ||||
| 		if (cv_reducevfx.value || leveltime % 2 == 0) | ||||
| 		{ | ||||
| 			K_SpawnDriftElectricity(player); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (leveltime % 2 == 1) | ||||
| 		return; | ||||
| 
 | ||||
|  | @ -6902,11 +6993,6 @@ static void K_SpawnDriftSparks(player_t *player) | |||
| 		P_SetTarget(&spark->owner, player->mo); | ||||
| 		spark->renderflags |= RF_REDUCEVFX; | ||||
| 	} | ||||
| 
 | ||||
| 	if (player->driftcharge >= dsthree) | ||||
| 	{ | ||||
| 		K_SpawnDriftElectricity(player); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void K_SpawnAIZDust(player_t *player) | ||||
|  | @ -7698,7 +7784,7 @@ void K_PuntMine(mobj_t *origMine, mobj_t *punter) | |||
| 
 | ||||
| 		if (mineOwner->player->itemamount) | ||||
| 		{ | ||||
| 			mineOwner->player->itemamount--; | ||||
| 			K_AdjustPlayerItemAmount(mineOwner->player, -1); | ||||
| 		} | ||||
| 
 | ||||
| 		if (!mineOwner->player->itemamount) | ||||
|  | @ -8268,7 +8354,7 @@ void K_PopPlayerShield(player_t *player) | |||
| 
 | ||||
| 	player->curshield = KSHIELD_NONE; | ||||
| 	player->itemtype = KITEM_NONE; | ||||
| 	player->itemamount = 0; | ||||
| 	K_SetPlayerItemAmount(player, 0); | ||||
| 	K_UnsetItemOut(player); | ||||
| } | ||||
| 
 | ||||
|  | @ -8516,9 +8602,9 @@ void K_DropHnextList(player_t *player) | |||
| 		player->itemflags &= ~IF_EGGMANOUT; | ||||
| 	} | ||||
| 	else if ((player->itemflags & IF_ITEMOUT) | ||||
| 		&& (dropall || (--player->itemamount <= 0))) | ||||
| 		&& (dropall || (K_AdjustPlayerItemAmount(player, -1) <= 0))) | ||||
| 	{ | ||||
| 		player->itemamount = 0; | ||||
| 		K_SetPlayerItemAmount(player, 0); | ||||
| 		K_UnsetItemOut(player); | ||||
| 		player->itemtype = KITEM_NONE; | ||||
| 	} | ||||
|  | @ -8773,7 +8859,7 @@ void K_RepairOrbitChain(mobj_t *orbit) | |||
| 		} | ||||
| 
 | ||||
| 		if (orbit->target && !P_MobjWasRemoved(orbit->target) && orbit->target->player->itemamount != num) | ||||
| 			orbit->target->player->itemamount = num; | ||||
| 			K_SetPlayerItemAmount(orbit->target->player, num); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -8926,7 +9012,7 @@ static void K_MoveHeldObjects(player_t *player) | |||
| 		} | ||||
| 		else if (player->itemflags & IF_ITEMOUT) | ||||
| 		{ | ||||
| 			player->itemamount = 0; | ||||
| 			K_SetPlayerItemAmount(player, 0); | ||||
| 			K_UnsetItemOut(player); | ||||
| 			player->itemtype = KITEM_NONE; | ||||
| 		} | ||||
|  | @ -8945,7 +9031,7 @@ static void K_MoveHeldObjects(player_t *player) | |||
| 		} | ||||
| 		else if (player->itemflags & IF_ITEMOUT) | ||||
| 		{ | ||||
| 			player->itemamount = 0; | ||||
| 			K_SetPlayerItemAmount(player, 0); | ||||
| 			K_UnsetItemOut(player); | ||||
| 			player->itemtype = KITEM_NONE; | ||||
| 		} | ||||
|  | @ -9171,12 +9257,12 @@ static void K_MoveHeldObjects(player_t *player) | |||
| // If we can move our backup item into main slots, do so.
 | ||||
| static void K_TryMoveBackupItem(player_t *player) | ||||
| { | ||||
| 	if (player->itemtype && player->itemtype == player->backupitemtype) | ||||
| 	if (player->itemtype && player->itemtype == player->backupitemtype && !(player->itemflags & IF_ITEMOUT)) | ||||
| 	{ | ||||
| 		player->itemamount += player->backupitemamount; | ||||
| 		K_AdjustPlayerItemAmount(player, player->backupitemamount); | ||||
| 
 | ||||
| 		player->backupitemtype = 0; | ||||
| 		player->backupitemamount = 0; | ||||
| 		K_SetPlayerBackupItemAmount(player, 0); | ||||
| 
 | ||||
| 		S_StartSound(player->mo, sfx_mbs54); | ||||
| 	} | ||||
|  | @ -9184,10 +9270,10 @@ static void K_TryMoveBackupItem(player_t *player) | |||
| 	if (player->itemtype == KITEM_NONE && player->backupitemtype && P_CanPickupItem(player, PICKUP_PAPERITEM)) | ||||
| 	{ | ||||
| 		player->itemtype = player->backupitemtype; | ||||
| 		player->itemamount = player->backupitemamount; | ||||
| 		K_SetPlayerItemAmount(player, player->backupitemamount); | ||||
| 
 | ||||
| 		player->backupitemtype = 0; | ||||
| 		player->backupitemamount = 0; | ||||
| 		K_SetPlayerBackupItemAmount(player, 0); | ||||
| 
 | ||||
| 		S_StartSound(player->mo, sfx_mbs54); | ||||
| 	} | ||||
|  | @ -10412,7 +10498,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) | |||
| 		{ | ||||
| 			player->rings = 0; | ||||
| 			player->itemtype = 0; | ||||
| 			player->itemamount = 0; | ||||
| 			K_SetPlayerItemAmount(player, 0); | ||||
| 			player->itemRoulette.active = false; | ||||
| 		} | ||||
| 	} | ||||
|  | @ -10424,12 +10510,18 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) | |||
| 		UINT8 counted = 0; | ||||
| 		UINT32 firstRaw = 0; | ||||
| 
 | ||||
| 		// We want the average position of the back quarter...
 | ||||
| 		UINT32 requiredPosition = ((D_NumPlayersInRace()*3)/4) - 1; | ||||
| 		// ...except in teamplay, where we want the true average.
 | ||||
| 		if (g_teamplay) | ||||
| 			requiredPosition = 1; | ||||
| 
 | ||||
| 		for (UINT8 i = 0; i < MAXPLAYERS; i++) | ||||
| 		{ | ||||
| 			if (playeringame[i] == false || players[i].spectator == true || players[i].exiting) | ||||
| 				continue; | ||||
| 
 | ||||
| 			if (players[i].position != 1 && players[i].position >= ((D_NumPlayersInRace()*3)/4) - 1) // Not in 1st, but also back quarter of the average (-1 guy, for leeway)
 | ||||
| 			if (players[i].position != 1 && players[i].position >= requiredPosition) | ||||
| 			{ | ||||
| 				counted++; | ||||
| 				average += K_UndoMapScaling(players[i].distancetofinish); | ||||
|  | @ -10451,6 +10543,15 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) | |||
| 		UINT32 TOO_CLOSE = average + 6500; // Start gaining here, lose if closer
 | ||||
| 		UINT32 WAY_TOO_CLOSE = average + 5500; // Lose at max rate here
 | ||||
| 
 | ||||
| 		fixed_t comeback = K_TeamComebackMultiplier(player); | ||||
| 
 | ||||
| 		if (comeback > FRACUNIT) | ||||
| 		{ | ||||
| 			REALLY_FAR = FixedDiv(REALLY_FAR, comeback); | ||||
| 			TOO_CLOSE = FixedDiv(TOO_CLOSE, comeback); | ||||
| 			WAY_TOO_CLOSE = FixedDiv(WAY_TOO_CLOSE, comeback); | ||||
| 		} | ||||
| 
 | ||||
| 		fixed_t MAX_GAIN_PER_SEC = FRACUNIT/20; // % assist to gain per sec when REALLY_FAR
 | ||||
| 		fixed_t MAX_LOSS_PER_SEC = FRACUNIT/5; // % assist to lose per sec when WAY_TOO_CLOSE
 | ||||
| 
 | ||||
|  | @ -10945,6 +11046,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) | |||
| 
 | ||||
| 		P_StartQuakeFromMobj(7, 50 * player->mo->scale, 2048 * player->mo->scale, player->mo); | ||||
| 		player->bailhitlag = false; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		if (player->markedfordeath) | ||||
| 			P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); | ||||
| 		*/ | ||||
| 	} | ||||
| 
 | ||||
| 	if ((!P_PlayerInPain(player) && player->bailcharge >= 5) || player->bailcharge >= BAIL_MAXCHARGE) | ||||
|  | @ -10952,6 +11058,13 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) | |||
| 		mobj_t *bail = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_BAIL); | ||||
| 		P_SetTarget(&bail->target, player->mo); | ||||
| 
 | ||||
| 		if ((player->itemRoulette.active && player->itemRoulette.eggman) || player->eggmanexplode > 0) | ||||
| 		{ | ||||
| 			player->markedfordeath = true; | ||||
| 			player->eggmanexplode = 1; | ||||
| 			player->rings = -20; | ||||
| 		} | ||||
| 
 | ||||
| 		if (player->itemRoulette.active) | ||||
| 		{ | ||||
| 			player->itemRoulette.active = false; | ||||
|  | @ -10961,7 +11074,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) | |||
| 		K_DeleteHnextList(player); | ||||
| 		K_DropItems(player); | ||||
| 
 | ||||
| 		player->itemamount = 0; | ||||
| 		K_SetPlayerItemAmount(player, 0); | ||||
| 		player->itemtype = 0; | ||||
| 		player->rocketsneakertimer = 0; | ||||
| 
 | ||||
|  | @ -10969,7 +11082,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) | |||
| 		if (player->itemamount) | ||||
| 		{ | ||||
| 			K_DropPaperItem(player, player->itemtype, player->itemamount); | ||||
| 			player->itemtype = player->itemamount = 0; | ||||
| 			player->itemtype = K_SetPlayerItemAmount(player, 0); | ||||
| 		} | ||||
| 		*/ | ||||
| 
 | ||||
|  | @ -11027,7 +11140,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) | |||
| 	if (player->preventfailsafe) | ||||
| 		player->preventfailsafe--; | ||||
| 
 | ||||
| 	if (player->tripwireUnstuck > 150) | ||||
| 	UINT8 unstuckthreshold = (onground) ? 80 : 40; | ||||
| 
 | ||||
| 	if (player->tripwireUnstuck > unstuckthreshold) | ||||
| 	{ | ||||
| 		player->tripwireUnstuck = 0; | ||||
| 		K_DoIngameRespawn(player); | ||||
|  | @ -11037,12 +11152,12 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) | |||
| 	{ | ||||
| 		if (P_IsDisplayPlayer(player)) | ||||
| 		{ | ||||
| 			S_StartSoundAtVolume(NULL, sfx_mbs43, 255); | ||||
| 			S_StartSoundAtVolume(NULL, sfx_mbs43, 255); | ||||
| 			S_StartSoundAtVolume(player->mo, sfx_mbs43, 255); | ||||
| 			S_StartSoundAtVolume(player->mo, sfx_mbs43, 255); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			S_StartSoundAtVolume(NULL, sfx_mbs43, 127); | ||||
| 			S_StartSoundAtVolume(player->mo, sfx_mbs43, 127); | ||||
| 		} | ||||
| 		player->amppickup--; | ||||
| 	} | ||||
|  | @ -11616,7 +11731,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) | |||
| 				// activate a mine while you're out of its radius,
 | ||||
| 				// the SAME tic it sets your itemamount to 0
 | ||||
| 				// ...:dumbestass:
 | ||||
| 				player->itemamount--; | ||||
| 				K_AdjustPlayerItemAmount(player, -1); | ||||
| 				K_PlayAttackTaunt(player->mo); | ||||
| 				player->botvars.itemconfirm = 0; | ||||
| 			} | ||||
|  | @ -11789,9 +11904,15 @@ void K_KartResetPlayerColor(player_t *player) | |||
| 
 | ||||
| 		fullbright = true; | ||||
| 
 | ||||
| 		int invinc_rotation_delay = 2; | ||||
| 		if (cv_reducevfx.value) | ||||
| 		{ | ||||
| 			invinc_rotation_delay = 8; | ||||
| 		} | ||||
| 
 | ||||
| 		if (player->invincibilitytimer > defaultTime) | ||||
| 		{ | ||||
| 			player->mo->color = K_RainbowColor(leveltime / 2); | ||||
| 			player->mo->color = K_RainbowColor(leveltime / invinc_rotation_delay); | ||||
| 			player->mo->colorized = true; | ||||
| 			skip = true; | ||||
| 		} | ||||
|  | @ -11800,7 +11921,7 @@ void K_KartResetPlayerColor(player_t *player) | |||
| 			flicker += (defaultTime - player->invincibilitytimer) / TICRATE / 2; | ||||
| 		} | ||||
| 
 | ||||
| 		if (leveltime % flicker == 0) | ||||
| 		if (leveltime % flicker == 0 && !cv_reducevfx.value) | ||||
| 		{ | ||||
| 			player->mo->color = SKINCOLOR_INVINCFLASH; | ||||
| 			player->mo->colorized = true; | ||||
|  | @ -11815,7 +11936,8 @@ void K_KartResetPlayerColor(player_t *player) | |||
| 
 | ||||
| 	if (player->growshrinktimer) // Ditto, for grow/shrink
 | ||||
| 	{ | ||||
| 		if (player->growshrinktimer % 5 == 0) | ||||
| 
 | ||||
| 		if ((!cv_reducevfx.value && player->growshrinktimer % 5 == 0) || (cv_reducevfx.value && player->growshrinktimer % 35 < 12)) | ||||
| 		{ | ||||
| 			player->mo->colorized = true; | ||||
| 			player->mo->color = (player->growshrinktimer < 0 ? SKINCOLOR_CREAMSICLE : SKINCOLOR_PERIWINKLE); | ||||
|  | @ -11846,13 +11968,18 @@ void K_KartResetPlayerColor(player_t *player) | |||
| 	} | ||||
| 
 | ||||
| 	boolean allowflashing = true; | ||||
| 	int flashingrate = 1; | ||||
| 	if (demo.playback && cv_reducevfx.value && !R_CanShowSkinInDemo(player->skin)) | ||||
| 	{ | ||||
| 		// messy condition stack for, specifically, disabling flashing effects when viewing a staff ghost replay of a currently hidden character
 | ||||
| 		allowflashing = false; | ||||
| 	} | ||||
| 	if (cv_reducevfx.value) | ||||
| 	{ | ||||
| 		flashingrate = 4; | ||||
| 	} | ||||
| 
 | ||||
| 	if (player->overdrive && (leveltime & 1) && allowflashing) | ||||
| 	if (player->overdrive && ((leveltime / flashingrate) & 1) && allowflashing) | ||||
| 	{ | ||||
| 		player->mo->colorized = true; | ||||
| 		fullbright = true; | ||||
|  | @ -11867,7 +11994,7 @@ void K_KartResetPlayerColor(player_t *player) | |||
| 		goto finalise; | ||||
| 	} | ||||
| 
 | ||||
| 	if (player->ringboost && (leveltime & 1) && allowflashing) // ring boosting
 | ||||
| 	if (player->ringboost && ((leveltime / flashingrate) & 1) && allowflashing) // ring boosting
 | ||||
| 	{ | ||||
| 		player->mo->colorized = true; | ||||
| 		fullbright = true; | ||||
|  | @ -13451,7 +13578,7 @@ void K_KartUpdatePosition(player_t *player) | |||
| 	{ | ||||
| 		// Ensure these are reset for spectators
 | ||||
| 		player->position = 0; | ||||
| 		player->positiondelay = 0; | ||||
| 		player->positiondelay = player->leaderpenalty = 0; | ||||
| 		player->teamposition = 0; | ||||
| 		player->teamimportance = 0; | ||||
| 		return; | ||||
|  | @ -13578,33 +13705,48 @@ void K_KartUpdatePosition(player_t *player) | |||
| 	} | ||||
| 
 | ||||
| 	/* except in FREE PLAY */ | ||||
| 	if (player->curshield == KSHIELD_TOP && | ||||
| 			(gametyperules & GTR_CIRCUIT) && | ||||
| 	if ((gametyperules & GTR_CIRCUIT) && | ||||
| 			realplayers > 1 && | ||||
| 			!specialstageinfo.valid | ||||
| 			&& !K_Cooperative()) | ||||
| 	{ | ||||
| 		/* grace period so you don't fall off INSTANTLY */ | ||||
| 		if (K_GetItemRouletteDistance(player, 8) < 2000 && player->topinfirst < 2*TICRATE) // "Why 8?" Literally no reason, but since we intend for constant-ish distance we choose a fake fixed playercount.
 | ||||
| 		if (position == 1) | ||||
| 		{ | ||||
| 			player->topinfirst++; | ||||
| 			// Hyuu and other leader-penalty
 | ||||
| 			if (player->leaderpenalty < POS_DELAY_TIME + 4) | ||||
| 				player->leaderpenalty++; | ||||
| 		} | ||||
| 		else | ||||
| 		else if (player->leaderpenalty != 0) | ||||
| 			player->leaderpenalty--; | ||||
| 
 | ||||
| 		if (player->curshield == KSHIELD_TOP) | ||||
| 		{ | ||||
| 			if (position == 1) | ||||
| 			/* grace period so you don't fall off INSTANTLY */ | ||||
| 			if (K_GetItemRouletteDistance(player, 8) < 2000 && player->topinfirst < 2*TICRATE) // "Why 8?" Literally no reason, but since we intend for constant-ish distance we choose a fake fixed playercount.
 | ||||
| 			{ | ||||
| 				Obj_GardenTopThrow(player); | ||||
| 				player->topinfirst++; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				if (player->topinfirst && (leveltime%3 == 0)) | ||||
| 					player->topinfirst--; | ||||
| 				if (position == 1) | ||||
| 				{ | ||||
| 					Obj_GardenTopThrow(player); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					if (player->topinfirst && (leveltime%3 == 0)) | ||||
| 						player->topinfirst--; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			player->topinfirst = 0; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		player->topinfirst = 0; | ||||
| 		player->leaderpenalty = player->topinfirst = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	player->position = position; | ||||
|  | @ -13687,11 +13829,11 @@ void K_StripItems(player_t *player) | |||
| 	K_DropRocketSneaker(player); | ||||
| 	K_DropKitchenSink(player); | ||||
| 	player->itemtype = KITEM_NONE; | ||||
| 	player->itemamount = 0; | ||||
| 	K_SetPlayerItemAmount(player, 0); | ||||
| 	player->itemflags &= ~(IF_ITEMOUT|IF_EGGMANOUT); | ||||
| 
 | ||||
| 	player->backupitemtype = KITEM_NONE; | ||||
| 	player->backupitemamount = 0; | ||||
| 	K_SetPlayerBackupItemAmount(player, 0); | ||||
| 
 | ||||
| 	if (player->itemRoulette.eggman == false) | ||||
| 	{ | ||||
|  | @ -15193,7 +15335,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 						{ | ||||
| 							player->momentboost += 3; | ||||
| 							angle_t flingangle = player->mo->angle + ((P_RandomByte(PR_ITEM_RINGS) & 1) ? -ANGLE_90 : ANGLE_90); | ||||
| 							P_FlingBurst(player, flingangle, MT_DEBTSPIKE, 0, 3 * FRACUNIT / 2, 20, 4*FRACUNIT); | ||||
| 							P_FlingBurst(player, flingangle, MT_DEBTSPIKE, TICRATE/2, 3 * FRACUNIT / 2, 20, 4*FRACUNIT); | ||||
| 							S_StartSound(player->mo, sfx_gshae); | ||||
| 						} | ||||
| 
 | ||||
|  | @ -15255,6 +15397,10 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 				{ | ||||
| 					K_UnsetItemOut(player); | ||||
| 				} | ||||
| 				else if (player->bungee) | ||||
| 				{ | ||||
| 					// michael_jordan.mov
 | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					switch (player->itemtype) | ||||
|  | @ -15264,7 +15410,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 							{ | ||||
| 								K_DoSneaker(player, 1); | ||||
| 								K_PlayBoostTaunt(player->mo); | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								player->botvars.itemconfirm = 0; | ||||
| 							} | ||||
| 							break; | ||||
|  | @ -15282,7 +15428,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 								//K_DoSneaker(player, 2);
 | ||||
| 
 | ||||
| 								player->rocketsneakertimer = (itemtime*3); | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_UpdateHnextList(player, true); | ||||
| 
 | ||||
| 								for (moloop = 0; moloop < 2; moloop++) | ||||
|  | @ -15313,7 +15459,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 									max(7u * TICRATE + behindScaled, player->invincibilitytimer + 5u*TICRATE)); | ||||
| 								K_PlayPowerGloatSound(player->mo); | ||||
| 
 | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								player->botvars.itemconfirm = 0; | ||||
| 							} | ||||
| 							break; | ||||
|  | @ -15333,7 +15479,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 									mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BANANA_SHIELD); | ||||
| 									if (!mo) | ||||
| 									{ | ||||
| 										player->itemamount = moloop; | ||||
| 										K_SetPlayerItemAmount(player, moloop); | ||||
| 										break; | ||||
| 									} | ||||
| 									mo->flags |= MF_NOCLIPTHING; | ||||
|  | @ -15350,7 +15496,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 							} | ||||
| 							else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Banana x3 thrown
 | ||||
| 							{ | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_ThrowKartItem(player, false, MT_BANANA, -1, 0, 0); | ||||
| 								K_PlayAttackTaunt(player->mo); | ||||
| 								K_UpdateHnextList(player, false); | ||||
|  | @ -15361,7 +15507,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 							if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) | ||||
| 							{ | ||||
| 								mobj_t *mo; | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								player->itemflags |= IF_EGGMANOUT; | ||||
| 								S_StartSound(player->mo, sfx_s254); | ||||
| 								mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM_SHIELD); | ||||
|  | @ -15397,7 +15543,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 									mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ORBINAUT_SHIELD); | ||||
| 									if (!mo) | ||||
| 									{ | ||||
| 										player->itemamount = moloop; | ||||
| 										K_SetPlayerItemAmount(player, moloop); | ||||
| 										break; | ||||
| 									} | ||||
| 									mo->flags |= MF_NOCLIPTHING; | ||||
|  | @ -15416,7 +15562,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 							} | ||||
| 							else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Orbinaut x3 thrown
 | ||||
| 							{ | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0, 0); | ||||
| 								K_PlayAttackTaunt(player->mo); | ||||
| 								K_UpdateHnextList(player, false); | ||||
|  | @ -15441,7 +15587,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 									mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_JAWZ_SHIELD); | ||||
| 									if (!mo) | ||||
| 									{ | ||||
| 										player->itemamount = moloop; | ||||
| 										K_SetPlayerItemAmount(player, moloop); | ||||
| 										break; | ||||
| 									} | ||||
| 									mo->flags |= MF_NOCLIPTHING; | ||||
|  | @ -15459,7 +15605,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 							} | ||||
| 							else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Jawz thrown
 | ||||
| 							{ | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_ThrowKartItem(player, true, MT_JAWZ, 1, 0, 0); | ||||
| 								K_PlayAttackTaunt(player->mo); | ||||
| 								K_UpdateHnextList(player, false); | ||||
|  | @ -15487,7 +15633,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 							} | ||||
| 							else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) | ||||
| 							{ | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_ThrowKartItem(player, false, MT_SSMINE, 1, 1, 0); | ||||
| 								K_PlayAttackTaunt(player->mo); | ||||
| 								player->itemflags &= ~IF_ITEMOUT; | ||||
|  | @ -15498,7 +15644,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 						case KITEM_LANDMINE: | ||||
| 							if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) | ||||
| 							{ | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								if (player->throwdir > 0) | ||||
| 								{ | ||||
| 									K_ThrowKartItem(player, true, MT_LANDMINE, -1, 0, 0); | ||||
|  | @ -15533,7 +15679,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 							} | ||||
| 							else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) | ||||
| 							{ | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								mobj_t *drop = K_ThrowKartItem(player, (player->throwdir > 0), MT_DROPTARGET, -1, 0, 0); | ||||
| 								P_SetTarget(&drop->tracer, player->mo); | ||||
| 								K_PlayAttackTaunt(player->mo); | ||||
|  | @ -15631,8 +15777,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 												P_SetObjectMomZ(player->mo, -50*FRACUNIT, true); | ||||
| 											} | ||||
| 											*/ | ||||
| 
 | ||||
| 											player->itemamount = 0; | ||||
| 											K_SetPlayerItemAmount(player, 0); | ||||
| 											player->botvars.itemconfirm = 0; | ||||
| 											player->ballhogcharge = 0; | ||||
| 											player->ballhogburst = 0; | ||||
|  | @ -15647,8 +15792,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 										if (numhogs > 0) // no tapfire scams
 | ||||
| 										{ | ||||
| 											K_SetItemOut(player); // need this to set itemscale
 | ||||
| 
 | ||||
| 											player->itemamount -= numhogs; | ||||
| 											K_AdjustPlayerItemAmount(player, -numhogs); | ||||
| 											K_PlayAttackTaunt(player->mo); | ||||
| 											K_DoBallhogAttack(player, numhogs); | ||||
| 
 | ||||
|  | @ -15678,7 +15822,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 						case KITEM_SPB: | ||||
| 							if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) | ||||
| 							{ | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_SetItemOut(player); | ||||
| 								K_ThrowKartItem(player, true, MT_SPB, 1, 0, 0); | ||||
| 								K_UnsetItemOut(player); | ||||
|  | @ -15718,7 +15862,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 
 | ||||
| 								S_StartSound(player->mo, sfx_kc5a); | ||||
| 
 | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								player->botvars.itemconfirm = 0; | ||||
| 							} | ||||
| 							break; | ||||
|  | @ -15726,7 +15870,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 							if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) | ||||
| 							{ | ||||
| 								K_DoShrink(player); | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_PlayPowerGloatSound(player->mo); | ||||
| 								player->botvars.itemconfirm = 0; | ||||
| 							} | ||||
|  | @ -15841,7 +15985,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 
 | ||||
| 									if (player->bubbleblowup > bubbletime*2) | ||||
| 									{ | ||||
| 										player->itemamount--; | ||||
| 										K_AdjustPlayerItemAmount(player, -1); | ||||
| 
 | ||||
| 										if (player->throwdir == -1) | ||||
| 										{ | ||||
|  | @ -15947,7 +16091,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 										player->flamemeter = 0; | ||||
| 										player->flamelength = 0; | ||||
| 										player->itemflags &= ~IF_HOLDREADY; | ||||
| 										player->itemamount--; | ||||
| 										K_AdjustPlayerItemAmount(player, -1); | ||||
| 									} | ||||
| 								} | ||||
| 								else | ||||
|  | @ -15981,7 +16125,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 						case KITEM_HYUDORO: | ||||
| 							if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) | ||||
| 							{ | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								//K_DoHyudoroSteal(player); // yes. yes they do.
 | ||||
| 								Obj_HyudoroDeploy(player->mo); | ||||
| 								K_PlayAttackTaunt(player->mo); | ||||
|  | @ -15994,7 +16138,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 								K_PlayBoostTaunt(player->mo); | ||||
| 								//K_DoPogoSpring(player->mo, 32<<FRACBITS, 2);
 | ||||
| 								P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_POGOSPRING); | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								player->botvars.itemconfirm = 0; | ||||
| 							} | ||||
| 							break; | ||||
|  | @ -16037,7 +16181,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 								// player->strongdriftboost += TICRATE;
 | ||||
| 								// player->driftboost += TICRATE;
 | ||||
| 								K_AwardPlayerRings(player, 20*player->itemamount, true); | ||||
| 								player->itemamount = 0; | ||||
| 								K_SetPlayerItemAmount(player, 0); | ||||
| 								player->botvars.itemconfirm = 0; | ||||
| 							} | ||||
| 							break; | ||||
|  | @ -16062,7 +16206,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 							} | ||||
| 							else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Sink thrown
 | ||||
| 							{ | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_ThrowKartItem(player, false, MT_SINK, 1, 2, 0); | ||||
| 								K_PlayAttackTaunt(player->mo); | ||||
| 								player->itemflags &= ~IF_ITEMOUT; | ||||
|  | @ -16073,7 +16217,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 						case KITEM_GACHABOM: | ||||
| 							if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) | ||||
| 							{ | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_SetItemOut(player); // need this to set itemscale
 | ||||
| 								K_ThrowKartItem(player, true, MT_GACHABOM, 0, 0, 0); | ||||
| 								K_UnsetItemOut(player); | ||||
|  | @ -16111,7 +16255,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 									K_UnsetItemOut(player); | ||||
| 								} | ||||
| 
 | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_PlayAttackTaunt(player->mo); | ||||
| 								K_UpdateHnextList(player, false); | ||||
| 								player->botvars.itemconfirm = 0; | ||||
|  | @ -16127,7 +16271,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 
 | ||||
| 								K_UnsetItemOut(player); | ||||
| 
 | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								K_PlayAttackTaunt(player->mo); | ||||
| 								player->botvars.itemconfirm = 0; | ||||
| 							} | ||||
|  | @ -16137,7 +16281,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) | |||
| 								&& !player->sadtimer) | ||||
| 							{ | ||||
| 								player->sadtimer = stealtime; | ||||
| 								player->itemamount--; | ||||
| 								K_AdjustPlayerItemAmount(player, -1); | ||||
| 								player->botvars.itemconfirm = 0; | ||||
| 							} | ||||
| 							break; | ||||
|  | @ -17553,20 +17697,20 @@ static boolean K_PickUp(player_t *player, mobj_t *picked) | |||
| 	if (player->itemtype == type && player->itemamount && !(player->itemflags & IF_ITEMOUT)) | ||||
| 	{ | ||||
| 		// We have this item in main slot but not deployed, just add it
 | ||||
| 		player->itemamount += amount; | ||||
| 		K_SetPlayerItemAmount(player, player->itemamount + amount); | ||||
| 	} | ||||
| 	else if (player->backupitemamount && player->backupitemtype) | ||||
| 	{ | ||||
| 		// We already have a backup item, stack it if it can be stacked or discard it
 | ||||
| 		if (player->backupitemtype == type) | ||||
| 		{ | ||||
| 			player->backupitemamount += amount; | ||||
| 			K_AdjustPlayerBackupItemAmount(player, amount); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			K_DropPaperItem(player, player->backupitemtype, player->backupitemamount); | ||||
| 			player->backupitemtype = type; | ||||
| 			player->backupitemamount = amount; | ||||
| 			K_SetPlayerBackupItemAmount(player, amount); | ||||
| 			S_StartSound(player->mo, sfx_kc65); | ||||
| 		} | ||||
| 	} | ||||
|  | @ -17574,7 +17718,7 @@ static boolean K_PickUp(player_t *player, mobj_t *picked) | |||
| 	{ | ||||
| 		// We have no backup item, load one up
 | ||||
| 		player->backupitemtype = type; | ||||
| 		player->backupitemamount = amount; | ||||
| 		K_SetPlayerBackupItemAmount(player, amount); | ||||
| 	} | ||||
| 
 | ||||
| 	S_StartSound(player->mo, sfx_aple); | ||||
|  |  | |||
|  | @ -41,7 +41,7 @@ Make sure this matches the actual number of states | |||
| #define INSTAWHIP_RINGDRAINEVERY (TICRATE/2) | ||||
| #define INSTAWHIP_HOLD_DELAY (TICRATE*2) | ||||
| // MUST be longer or equal to INSTAWHIP_CHARGETIME.
 | ||||
| #define INSTAWHIP_TETHERBLOCK (TICRATE*4) | ||||
| #define INSTAWHIP_TETHERBLOCK (3*TICRATE/4) | ||||
| #define PUNISHWINDOW (G_CompatLevel(0x0010) ? 7*TICRATE/10 : 10*TICRATE/10) | ||||
| 
 | ||||
| #define BAIL_MAXCHARGE (84) // tics to bail when in painstate nad in air, on ground is half, if you touch this, also update Obj_BailChargeThink synced animation logic
 | ||||
|  | @ -125,6 +125,11 @@ Make sure this matches the actual number of states | |||
| #define AUTORESPAWN_TIME (10*TICRATE) | ||||
| #define AUTORESPAWN_THRESHOLD (7*TICRATE) | ||||
| 
 | ||||
| UINT8 K_SetPlayerItemAmount(player_t *player, INT32 amount); | ||||
| UINT8 K_SetPlayerBackupItemAmount(player_t *player, INT32 amount); | ||||
| UINT8 K_AdjustPlayerItemAmount(player_t *player, INT32 amount); | ||||
| UINT8 K_AdjustPlayerBackupItemAmount(player_t *player, INT32 amount); | ||||
| 
 | ||||
| angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t yourspeed); | ||||
| 
 | ||||
| void K_PopBubbleShield(player_t *player); | ||||
|  |  | |||
|  | @ -748,7 +748,7 @@ void M_SetMenuDelay(UINT8 i); | |||
| 
 | ||||
| void M_SortServerList(void); | ||||
| 
 | ||||
| void M_UpdateMenuCMD(UINT8 i, boolean bailrequired); | ||||
| void M_UpdateMenuCMD(UINT8 i, boolean bailrequired, boolean chat_open); | ||||
| boolean M_Responder(event_t *ev); | ||||
| boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt); | ||||
| boolean M_MenuButtonHeld(UINT8 pid, UINT32 bt); | ||||
|  | @ -961,6 +961,10 @@ void M_MenuToLevelPreamble(UINT8 ssplayers, boolean nowipe); | |||
| void M_LevelSelected(INT16 add, boolean menuupdate); | ||||
| boolean M_LevelSelectCupSwitch(boolean next, boolean skipones); | ||||
| 
 | ||||
| void M_LevelConfirmHandler(void); | ||||
| void M_ClearQueueHandler(void); | ||||
| void M_CupQueueHandler(cupheader_t *cup); | ||||
| 
 | ||||
| // dummy consvars for GP & match race setup
 | ||||
| extern consvar_t cv_dummygpdifficulty; | ||||
| extern consvar_t cv_dummykartspeed; | ||||
|  |  | |||
|  | @ -3340,6 +3340,21 @@ void M_DrawCupSelect(void) | |||
| 	M_DrawCupPreview(y, &templevelsearch); | ||||
| 
 | ||||
| 	M_DrawCupTitle(120 - ty, &templevelsearch); | ||||
| 	 | ||||
| 	const char *worktext = "Undo"; | ||||
| 	 | ||||
| 	if (menuqueue.size) | ||||
| 		worktext = "Undo"; | ||||
| 	else if (roundqueue.size) | ||||
| 		worktext = "Clear Queue"; | ||||
| 	 | ||||
| 	if (levellist.canqueue) | ||||
| 	{ | ||||
| 		K_DrawGameControl(BASEVIDWIDTH/2, 6-ty, 0, va("%s Queue Cup<white>   %s %s", | ||||
| 			(templevelsearch.cup && templevelsearch.cup != &dummy_lostandfound && !roundqueue.size) ? "<z_animated>" : "<z_pressed><gray>", | ||||
| 			(roundqueue.size || menuqueue.size) ? "<c_animated>" : "<c_pressed><gray>", | ||||
| 		worktext), 1, TINY_FONT, 0); | ||||
| 	} | ||||
| 
 | ||||
| 	if (templevelsearch.grandprix == false && templevelsearch.cup != NULL) | ||||
| 	{ | ||||
|  |  | |||
|  | @ -1084,7 +1084,7 @@ void M_SetMenuDelay(UINT8 i) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void M_UpdateMenuCMD(UINT8 i, boolean bailrequired) | ||||
| void M_UpdateMenuCMD(UINT8 i, boolean bailrequired, boolean chat_open) | ||||
| { | ||||
| 	UINT8 mp = max(1, setup_numplayers); | ||||
| 
 | ||||
|  | @ -1097,6 +1097,10 @@ void M_UpdateMenuCMD(UINT8 i, boolean bailrequired) | |||
| 	menucmd[i].buttonsHeld = menucmd[i].buttons; | ||||
| 	menucmd[i].buttons = 0; | ||||
| 
 | ||||
| 	// Eat inputs made when chat is open
 | ||||
| 	if (chat_open && pausemenu.closing) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (G_PlayerInputDown(i, gc_screenshot,    mp)) { menucmd[i].buttons |= MBT_SCREENSHOT; } | ||||
| 	if (G_PlayerInputDown(i, gc_startmovie,    mp)) { menucmd[i].buttons |= MBT_STARTMOVIE; } | ||||
| 	if (G_PlayerInputDown(i, gc_startlossless, mp)) { menucmd[i].buttons |= MBT_STARTLOSSLESS; } | ||||
|  |  | |||
|  | @ -1956,7 +1956,8 @@ void K_KartGetItemResult(player_t *const player, kartitems_t getitem) | |||
| 	UINT8 itemamount = K_ItemResultToAmount(getitem, &player->itemRoulette); | ||||
| 	if (cv_kartdebugitem.value != KITEM_NONE && cv_kartdebugitem.value == player->itemtype && cv_kartdebugamount.value > 1) | ||||
| 		itemamount = cv_kartdebugamount.value; | ||||
| 	player->itemamount = itemamount; | ||||
| 
 | ||||
| 	K_SetPlayerItemAmount(player, itemamount); | ||||
| 
 | ||||
| 	if (player->itemtype == KITEM_SPB) | ||||
| 		Obj_SPBEradicateCapsules(); | ||||
|  |  | |||
|  | @ -345,13 +345,10 @@ void level_tally_t::Init(player_t *player) | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if ((gametypes[gt]->rules & GTR_CIRCUIT) == GTR_CIRCUIT) | ||||
| 		if ((gametypes[gt]->rules & GTR_CIRCUIT) == GTR_CIRCUIT && K_GetNumGradingPoints() > 0) // EXP should be a rule type, but here we are
 | ||||
| 		{ | ||||
| 			if (player->exp) | ||||
| 			{ | ||||
| 				exp = player->exp; | ||||
| 				totalExp = EXP_TARGET; | ||||
| 			} | ||||
| 			exp = static_cast<UINT16>(std::max<fixed_t>(player->exp, 0)); // The scoring calc doesn't subtract anymore, so using 0 is okay and will not wrap
 | ||||
| 			totalExp = EXP_TARGET; | ||||
| 		} | ||||
| 
 | ||||
| 		if (battleprisons) | ||||
|  |  | |||
|  | @ -1845,7 +1845,7 @@ static void Y_TickVoteStageStrike(void) | |||
| static void Y_TickVoteSelection(void) | ||||
| { | ||||
| 	boolean everyone_voted = true;/* the default condition */ | ||||
| 	INT32 i; | ||||
| 	INT32 i, j; | ||||
| 
 | ||||
| 	if (vote.tic < 3*(NEWTICRATE/7)) // give it some time before letting you control it :V
 | ||||
| 	{ | ||||
|  | @ -1872,7 +1872,6 @@ static void Y_TickVoteSelection(void) | |||
| 				if (vote.players[i].sentTimeOutVote == false) | ||||
| 				{ | ||||
| 					// Move off of striked stages for the timeout vote.
 | ||||
| 					INT32 j; | ||||
| 					for (j = 0; j < VOTE_NUM_LEVELS; j++) | ||||
| 					{ | ||||
| 						if (g_votes_striked[vote.players[i].selection] == false) | ||||
|  | @ -1944,9 +1943,9 @@ static void Y_TickVoteSelection(void) | |||
| 			{ | ||||
| 				// bots vote randomly
 | ||||
| 				INT32 rng = M_RandomKey(VOTE_NUM_LEVELS); | ||||
| 				for (i = 0; i < VOTE_NUM_LEVELS; i++) | ||||
| 				for (j = 0; j < VOTE_NUM_LEVELS; j++) | ||||
| 				{ | ||||
| 					if (g_votes_striked[i] == false) | ||||
| 					if (g_votes_striked[j] == false) | ||||
| 					{ | ||||
| 						break; | ||||
| 					} | ||||
|  | @ -2095,7 +2094,7 @@ static void Y_InitVoteDrawing(void) | |||
| 	INT32 i = 0, j = 0; | ||||
| 
 | ||||
| 	vote_draw.ruby_icon = W_CachePatchName("RUBYICON", PU_STATIC); | ||||
| 	vote_draw.strike_icon = W_CachePatchName("K_NOBLNS", PU_STATIC); | ||||
| 	vote_draw.strike_icon = W_CachePatchName("VT_LSTRK", PU_STATIC); | ||||
| 
 | ||||
| 	for (i = 0; i < PLANET_FRAMES; i++) | ||||
| 	{ | ||||
|  |  | |||
|  | @ -46,6 +46,7 @@ | |||
| #include "k_hitlag.h" | ||||
| #include "music.h" // music functions necessary for lua integration | ||||
| #include "k_terrain.h" | ||||
| #include "k_grandprix.h" | ||||
| 
 | ||||
| #include "lua_script.h" | ||||
| #include "lua_libs.h" | ||||
|  | @ -256,6 +257,12 @@ static const struct { | |||
| 	{META_POWERUPVARS,	"powerupvars_t"}, | ||||
| 	{META_ICECUBEVARS,	"icecubevars_t"}, | ||||
| 	{META_SKYBOX,		"skybox_t"}, | ||||
| 	 | ||||
| 	{META_CUP,     					"cupheader_t"}, | ||||
| 	{META_GPRANK,	  	  			"gprank_t"}, | ||||
| 	{META_GPRANKLEVEL,	  			"gprank_level_t"}, | ||||
| 	{META_GPRANKLEVELPERPLAYER,	    "gprank_level_perplayer_t"}, | ||||
| 	{META_ROUNDENTRY,	    		"roundentry_t"}, | ||||
| 
 | ||||
| 	{NULL,              NULL} | ||||
| }; | ||||
|  | @ -480,6 +487,13 @@ static int lib_mMusicRemap(lua_State *L) | |||
| 	{ | ||||
| 		return LUA_ErrNoTune(L, tune_id); | ||||
| 	} | ||||
| 	 | ||||
| 	// Do not allow Lua to remap Stereo Mode tunes.
 | ||||
| 	if (strlen(tune_id) > 5 | ||||
| 	    && toupper(tune_id[0]) == 'S' && toupper(tune_id[1]) == 'T' && toupper(tune_id[2]) == 'E' && toupper(tune_id[3]) == 'R' && toupper(tune_id[4]) == 'E') | ||||
| 	{ | ||||
| 		return LUA_ErrStereo(L, tune_id); | ||||
| 	} | ||||
| 
 | ||||
| 	Music_Remap(tune_id, music_name); | ||||
| 
 | ||||
|  | @ -3991,6 +4005,84 @@ static int lib_kMomentumAngle(lua_State *L) | |||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int lib_kPvPAmpReward(lua_State *L) | ||||
| {	 | ||||
| 	UINT32 award = luaL_checkinteger(L, 1); | ||||
| 	player_t *attacker = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); | ||||
| 	player_t *defender = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); | ||||
| 	NOHUD | ||||
| 	INLEVEL | ||||
| 	if (!attacker || !defender) | ||||
| 		return LUA_ErrInvalid(L, "player_t"); | ||||
| 	lua_pushinteger(L, K_PvPAmpReward(award, attacker, defender)); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int lib_kSpawnAmps(lua_State *L) | ||||
| { | ||||
| 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
| 	UINT8 amps = luaL_checkinteger(L, 2); | ||||
| 	mobj_t *impact = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); | ||||
| 	NOHUD | ||||
| 	INLEVEL | ||||
| 	if (!player) | ||||
| 		return LUA_ErrInvalid(L, "player_t"); | ||||
| 	if (!impact) | ||||
| 		return LUA_ErrInvalid(L, "mobj_t"); | ||||
| 	K_SpawnAmps(player, amps, impact); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lib_kSpawnEXP(lua_State *L) | ||||
| { | ||||
| 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
| 	UINT8 exp = luaL_checkinteger(L, 2); | ||||
| 	mobj_t *impact = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); | ||||
| 	NOHUD | ||||
| 	INLEVEL | ||||
| 	if (!player) | ||||
| 		return LUA_ErrInvalid(L, "player_t"); | ||||
| 	if (!impact) | ||||
| 		return LUA_ErrInvalid(L, "mobj_t"); | ||||
| 	K_SpawnEXP(player, exp, impact); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lib_kAwardPlayerAmps(lua_State *L) | ||||
| { | ||||
| 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
| 	UINT8 amps = luaL_checkinteger(L, 2); | ||||
| 	NOHUD | ||||
| 	INLEVEL | ||||
| 	if (!player) | ||||
| 		return LUA_ErrInvalid(L, "player_t"); | ||||
| 	K_AwardPlayerAmps(player, amps); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lib_kOverdrive(lua_State *L) | ||||
| { | ||||
| 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
| 	NOHUD | ||||
| 	INLEVEL | ||||
| 	if (!player) | ||||
| 		return LUA_ErrInvalid(L, "player_t"); | ||||
| 	lua_pushboolean(L, K_Overdrive(player)); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int lib_kDefensiveOverdrive(lua_State *L) | ||||
| { | ||||
| 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
| 	NOHUD | ||||
| 	INLEVEL | ||||
| 	if (!player) | ||||
| 		return LUA_ErrInvalid(L, "player_t"); | ||||
| 	lua_pushboolean(L, K_DefensiveOverdrive(player)); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int lib_kDoInstashield(lua_State *L) | ||||
| { | ||||
| 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
|  | @ -5270,6 +5362,48 @@ static int lib_kPlayerCanUseItem(lua_State *L) | |||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int lib_kGetGradingFactorAdjustment(lua_State *L) | ||||
| { | ||||
| 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
| 	UINT32 gradingpoint = luaL_checkinteger(L, 2); | ||||
| 	INLEVEL | ||||
| 	NOHUD | ||||
| 	if (!player) | ||||
| 		return LUA_ErrInvalid(L, "player_t"); | ||||
| 	lua_pushfixed(L, K_GetGradingFactorAdjustment(player, gradingpoint)); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int lib_kGetGradingFactorMinMax(lua_State *L) | ||||
| { | ||||
| 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
| 	boolean max = luaL_checkboolean(L, 2); | ||||
| 	INLEVEL | ||||
| 	NOHUD | ||||
| 	if (!player) | ||||
| 		return LUA_ErrInvalid(L, "player_t"); | ||||
| 	lua_pushfixed(L, K_GetGradingFactorMinMax(player, max)); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int lib_kGetEXP(lua_State *L) | ||||
| { | ||||
| 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
| 	INLEVEL | ||||
| 	NOHUD | ||||
| 	if (!player) | ||||
| 		return LUA_ErrInvalid(L, "player_t"); | ||||
| 	lua_pushinteger(L, K_GetEXP(player)); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int lib_kGetNumGradingPoints(lua_State *L) | ||||
| { | ||||
| 	INLEVEL | ||||
| 	lua_pushinteger(L, K_GetNumGradingPoints()); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int lib_kEggmanTransfer(lua_State *L) | ||||
| { | ||||
| 	player_t *source = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
|  | @ -5294,6 +5428,31 @@ static int lib_kSetTireGrease(lua_State *L) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lib_kApplyStun(lua_State *L) | ||||
| { | ||||
| 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); | ||||
| 	mobj_t *inflictor = NULL; | ||||
| 	mobj_t *source = NULL; | ||||
| 	INT32 damage = luaL_optinteger(L, 4, 0); | ||||
| 	UINT8 damagetype = luaL_optinteger(L, 5, 0); | ||||
| 	INLEVEL | ||||
| 	NOHUD | ||||
| 	if (!player) | ||||
| 		return LUA_ErrInvalid(L, "player_t"); | ||||
| 	if (!lua_isnil(L, 2) && lua_isuserdata(L, 2)) { | ||||
| 		inflictor = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); | ||||
| 		if (!inflictor) | ||||
| 			return LUA_ErrInvalid(L, "mobj_t"); | ||||
| 	} | ||||
| 	if (!lua_isnil(L, 3) && lua_isuserdata(L, 3)) { | ||||
| 		source = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); | ||||
| 		if (!source) | ||||
| 			return LUA_ErrInvalid(L, "mobj_t"); | ||||
| 	} | ||||
| 	K_ApplyStun(player, inflictor, source, damage, damagetype); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lib_kGetCollideAngle(lua_State *L) | ||||
| { | ||||
| 	mobj_t *t1 = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); | ||||
|  | @ -6786,7 +6945,13 @@ static luaL_Reg lib[] = { | |||
| 	{"K_MomentumAngleEx",lib_kMomentumAngleEx}, | ||||
| 	{"K_MomentumAngleReal",lib_kMomentumAngleReal}, | ||||
| 	{"K_MomentumAngle",lib_kMomentumAngle}, | ||||
| 	{"K_PvPAmpReward",lib_kPvPAmpReward}, | ||||
| 	{"K_SpawnAmps",lib_kSpawnAmps}, | ||||
| 	{"K_SpawnEXP",lib_kSpawnEXP}, | ||||
| 	{"K_AwardPlayerAmps",lib_kAwardPlayerAmps}, | ||||
| 	{"K_AwardPlayerRings",lib_kAwardPlayerRings}, | ||||
| 	{"K_Overdrive",lib_kOverdrive}, | ||||
| 	{"K_DefensiveOverdrive",lib_kDefensiveOverdrive}, | ||||
| 	{"K_DoInstashield",lib_kDoInstashield}, | ||||
| 	{"K_DoPowerClash",lib_kDoPowerClash}, | ||||
| 	{"K_DoGuardBreak",lib_kDoGuardBreak}, | ||||
|  | @ -6895,10 +7060,15 @@ static luaL_Reg lib[] = { | |||
| 	{"K_BumperInflate",lib_kBumperInflate}, | ||||
| 	{"K_ThunderDome",lib_kThunderDome}, | ||||
| 	{"K_PlayerCanUseItem",lib_kPlayerCanUseItem}, | ||||
| 	{"K_GetGradingFactorAdjustment",lib_kGetGradingFactorAdjustment}, | ||||
| 	{"K_GetGradingFactorMinMax",lib_kGetGradingFactorMinMax}, | ||||
| 	{"K_GetEXP",lib_kGetEXP}, | ||||
| 	{"K_GetNumGradingPoints",lib_kGetNumGradingPoints}, | ||||
| 	{"K_PlayerGuard",lib_kPlayerGuard}, | ||||
| 	{"K_FastFallBounce",lib_kFastFallBounce}, | ||||
| 	{"K_EggmanTransfer",lib_kEggmanTransfer}, | ||||
| 	{"K_SetTireGrease",lib_kSetTireGrease}, | ||||
| 	{"K_ApplyStun",lib_kApplyStun}, | ||||
| 
 | ||||
| 	{"K_GetCollideAngle",lib_kGetCollideAngle}, | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										968
									
								
								src/lua_grandprixlib.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										968
									
								
								src/lua_grandprixlib.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,968 @@ | |||
| // DR. ROBOTNIK'S RING RACERS
 | ||||
| //-----------------------------------------------------------------------------
 | ||||
| // Copyright (C) 2025 by Freaky Mutant Man.
 | ||||
| // Copyright (C) 2025 by Kart Krew.
 | ||||
| // Copyright (C) 2020 by Sonic Team Junior.
 | ||||
| // Copyright (C) 2016 by John "JTE" Muniz.
 | ||||
| //
 | ||||
| // 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  lua_grandprixlib.c
 | ||||
| /// \brief Grand Prix, cup and rank info for Lua scripting.
 | ||||
| 
 | ||||
| #include "doomdef.h" | ||||
| #include "fastcmp.h" | ||||
| #include "doomstat.h" | ||||
| #include "k_grandprix.h" | ||||
| #include "k_rank.h" | ||||
| #include "g_game.h" | ||||
| 
 | ||||
| #include "lua_script.h" | ||||
| #include "lua_libs.h" | ||||
| 
 | ||||
| #define UNIMPLEMENTED luaL_error(L, LUA_QL("cupheader_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", cup_opt[field]) | ||||
| #define RNOFIELDGP luaL_error(L, LUA_QL("grandprixinfo") " has no field named " LUA_QS, field) | ||||
| #define RNOFIELDCH luaL_error(L, LUA_QL("cupheader_t") " has no field named " LUA_QS, field) | ||||
| #define RNOFIELDGR luaL_error(L, LUA_QL("gprank_t") " has no field named " LUA_QS, field) | ||||
| #define RNOFIELDGRL luaL_error(L, LUA_QL("gprank_level_t") " has no field named " LUA_QS, field) | ||||
| #define RNOFIELDGRLP luaL_error(L, LUA_QL("gprank_level_perplayer_t") " has no field named " LUA_QS, field) | ||||
| #define RNOFIELDRQ luaL_error(L, LUA_QL("roundqueue") " has no field named " LUA_QS, field) | ||||
| #define RNOFIELDRE luaL_error(L, LUA_QL("roundentry_t") " has no field named " LUA_QS, field) | ||||
| #define GPERR luaL_error(L, LUA_QL("grandprixinfo") " field " LUA_QS " cannot be accessed while grandprixinfo.gp is false.", grandprix_opt[field]) | ||||
| #define ROUNDCUEERR luaL_error(L, LUA_QL("roundqueue") " field " LUA_QS " cannot be accessed while roundqueue.size is 0.", grandprix_opt[field]) | ||||
| 
 | ||||
| enum grandprix { | ||||
| 	grandprix_gp = 0, | ||||
| 	grandprix_cup, | ||||
| 	grandprix_gamespeed, | ||||
| 	grandprix_encore, | ||||
| 	grandprix_masterbots, | ||||
| 	grandprix_initialize, | ||||
| 	grandprix_initalize, | ||||
| 	grandprix_wonround, | ||||
| 	grandprix_eventmode, | ||||
| 	grandprix_specialdamage, | ||||
| 	grandprix_rank, | ||||
| }; | ||||
| 
 | ||||
| enum cup { | ||||
| 	cup_valid = 0, | ||||
| 	cup_id, | ||||
| 	cup_monitor, | ||||
| 	cup_name, | ||||
| 	cup_namehash, | ||||
| 	cup_realname, | ||||
| 	cup_icon, | ||||
| 	cup_levellist, | ||||
| 	cup_cachedlevels, | ||||
| 	cup_numlevels, | ||||
| 	cup_numbonus, | ||||
| 	cup_emeraldnum, | ||||
| 	cup_playcredits, | ||||
| 	cup_hintcondition, | ||||
| 	cup_cache_cuplock, | ||||
| 	cup_windata, | ||||
| 	cup_next, | ||||
| }; | ||||
| 
 | ||||
| enum gprank { | ||||
| 	gprank_valid = 0, | ||||
| 	gprank_numplayers, | ||||
| 	gprank_totalplayers, | ||||
| 	gprank_position, | ||||
| 	gprank_skin, | ||||
| 	gprank_winpoints, | ||||
| 	gprank_totalpoints, | ||||
| 	gprank_exp, | ||||
| 	gprank_totalexp, | ||||
| 	gprank_continuesused, | ||||
| 	gprank_prisons, | ||||
| 	gprank_totalprisons, | ||||
| 	gprank_rings, | ||||
| 	gprank_totalrings, | ||||
| 	gprank_specialwon, | ||||
| 	gprank_scoreposition, | ||||
| 	gprank_scoregppoints, | ||||
| 	gprank_scoreexp, | ||||
| 	gprank_scoreprisons, | ||||
| 	gprank_scorerings, | ||||
| 	gprank_scorecontinues, | ||||
| 	gprank_scoretotal, | ||||
| 	gprank_numlevels, | ||||
| 	gprank_levels, | ||||
| }; | ||||
| 
 | ||||
| enum gprank_level { | ||||
| 	gprank_level_valid = 0, | ||||
| 	gprank_level_id, | ||||
| 	gprank_level_event, | ||||
| 	gprank_level_time, | ||||
| 	gprank_level_totalexp, | ||||
| 	gprank_level_totalprisons, | ||||
| 	gprank_level_continues, | ||||
| 	gprank_level_perplayer, | ||||
| }; | ||||
| 
 | ||||
| enum gprank_level_perplayer { | ||||
| 	gprank_level_perplayer_valid = 0, | ||||
| 	gprank_level_perplayer_position, | ||||
| 	gprank_level_perplayer_rings, | ||||
| 	gprank_level_perplayer_exp, | ||||
| 	gprank_level_perplayer_prisons, | ||||
| 	gprank_level_perplayer_gotspecialprize, | ||||
| 	gprank_level_perplayer_grade, | ||||
| }; | ||||
| 
 | ||||
| enum roundcue { // named "roundcue" to avoid overlap with actual roundqueue struct
 | ||||
| 	roundcue_size = 0, // placed first since we'll be checking this to see if the roundqueue is currently in use
 | ||||
| 	roundcue_roundnum, | ||||
| 	roundcue_position, | ||||
| 	roundcue_netcommunicate, | ||||
| 	roundcue_writetextmap, | ||||
| 	roundcue_snapshotmaps, | ||||
| 	roundcue_entries, | ||||
| }; | ||||
| 
 | ||||
| enum roundentry { | ||||
| 	roundentry_valid = 0, | ||||
| 	roundentry_mapnum, | ||||
| 	roundentry_gametype, | ||||
| 	roundentry_encore, | ||||
| 	roundentry_rankrestricted, | ||||
| 	roundentry_overridden, | ||||
| }; | ||||
| 
 | ||||
| static const char *const grandprix_opt[] = { | ||||
| 	"gp", | ||||
| 	"cup", | ||||
| 	"gamespeed", | ||||
| 	"encore", | ||||
| 	"masterbots", | ||||
| 	"initialize", | ||||
| 	"initalize", | ||||
| 	"wonround", | ||||
| 	"eventmode", | ||||
| 	"specialdamage", | ||||
| 	"rank", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const char *const cup_opt[] = { | ||||
| 	"valid", | ||||
| 	"id", | ||||
| 	"monitor", | ||||
| 	"name", | ||||
| 	"namehash", | ||||
| 	"realname", | ||||
| 	"icon", | ||||
| 	"levellist", | ||||
| 	"cachedlevels", | ||||
| 	"numlevels", | ||||
| 	"numbonus", | ||||
| 	"emeraldnum", | ||||
| 	"playcredits", | ||||
| 	"hintcondition", | ||||
| 	"cache_cuplock", | ||||
| 	"windata", | ||||
| 	"next", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const char *const gprank_opt[] = { | ||||
| 	"valid", | ||||
| 	"numplayers", | ||||
| 	"totalplayers", | ||||
| 	"position", | ||||
| 	"skin", | ||||
| 	"winpoints", | ||||
| 	"totalpoints", | ||||
| 	"exp", | ||||
| 	"totalexp", | ||||
| 	"continuesused", | ||||
| 	"prisons", | ||||
| 	"totalprisons", | ||||
| 	"rings", | ||||
| 	"totalrings", | ||||
| 	"specialwon", | ||||
| 	"scoreposition", | ||||
| 	"scoregppoints", | ||||
| 	"scoreexp", | ||||
| 	"scoreprisons", | ||||
| 	"scorerings", | ||||
| 	"scorecontinues", | ||||
| 	"scoretotal", | ||||
| 	"numlevels", | ||||
| 	"levels", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const char *const gprank_level_opt[] = { | ||||
| 	"valid", | ||||
| 	"id", | ||||
| 	"event", | ||||
| 	"time", | ||||
| 	"totalexp", | ||||
| 	"totalprisons", | ||||
| 	"continues", | ||||
| 	"perplayer", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const char *const gprank_level_perplayer_opt[] = { | ||||
| 	"valid", | ||||
| 	"position", | ||||
| 	"rings", | ||||
| 	"exp", | ||||
| 	"prisons", | ||||
| 	"gotspecialprize", | ||||
| 	"grade", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const char *const roundcue_opt[] = { | ||||
| 	"size", | ||||
| 	"roundnum", | ||||
| 	"position", | ||||
| 	"netcommunicate", | ||||
| 	"writetextmap", | ||||
| 	"snapshotmaps", | ||||
| 	"entries", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const char *const roundentry_opt[] = { | ||||
| 	"valid", | ||||
| 	"mapnum", | ||||
| 	"gametype", | ||||
| 	"encore", | ||||
| 	"rankrestricted", | ||||
| 	"overridden", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static int grandprix_get(lua_State *L) | ||||
| { | ||||
| 	enum grandprix field = luaL_checkoption(L, 2, grandprix_opt[0], grandprix_opt); | ||||
| 	 | ||||
| 	// Don't return any grandprixinfo values while not in a GP.
 | ||||
| 	if (!grandprixinfo.gp) | ||||
| 	{ | ||||
| 		switch (field) | ||||
| 		{ | ||||
| 			case grandprix_gp: | ||||
| 				lua_pushboolean(L, false); | ||||
| 				return 1; | ||||
| 			default: | ||||
| 				return GPERR; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	switch (field) | ||||
| 	{ | ||||
| 	case grandprix_gp: | ||||
| 		lua_pushboolean(L, grandprixinfo.gp); | ||||
| 		break; | ||||
| 	case grandprix_cup: | ||||
| 		LUA_PushUserdata(L, grandprixinfo.cup, META_CUP); | ||||
| 		break; | ||||
| 	case grandprix_gamespeed: | ||||
| 		lua_pushnumber(L, grandprixinfo.gamespeed); | ||||
| 		break; | ||||
| 	case grandprix_encore: | ||||
| 		lua_pushboolean(L, grandprixinfo.encore); | ||||
| 		break; | ||||
| 	case grandprix_masterbots: | ||||
| 		lua_pushboolean(L, grandprixinfo.masterbots); | ||||
| 		break; | ||||
| 	case grandprix_initialize: | ||||
| 	case grandprix_initalize: // when the struct misspelled the variable...
 | ||||
| 		lua_pushboolean(L, grandprixinfo.initalize); | ||||
| 		break; | ||||
| 	case grandprix_wonround: | ||||
| 		lua_pushboolean(L, grandprixinfo.wonround); | ||||
| 		break; | ||||
| 	case grandprix_eventmode: | ||||
| 		lua_pushnumber(L, grandprixinfo.eventmode); | ||||
| 		break; | ||||
| 	case grandprix_specialdamage: | ||||
| 		lua_pushnumber(L, grandprixinfo.specialDamage); | ||||
| 		break; | ||||
| 	case grandprix_rank: | ||||
| 		LUA_PushUserdata(L, &grandprixinfo.rank, META_GPRANK); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return RNOFIELDGP; | ||||
| 	} | ||||
| 	 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int grandprix_set(lua_State *L) | ||||
| { | ||||
| 	return luaL_error(L, LUA_QL("grandprixinfo") " struct cannot be edited by Lua."); | ||||
| } | ||||
| 
 | ||||
| static int cup_get(lua_State *L) | ||||
| { | ||||
| 	cupheader_t *cup = *((cupheader_t **)luaL_checkudata(L, 1, META_CUP)); | ||||
| 	enum cup field = luaL_checkoption(L, 2, cup_opt[0], cup_opt); | ||||
| 	 | ||||
| 	if (!cup) | ||||
| 	{ | ||||
| 		switch (field) | ||||
| 		{ | ||||
| 			case cup_valid: | ||||
| 				lua_pushboolean(L, false); | ||||
| 				return 1; | ||||
| 			default: | ||||
| 				return LUA_ErrInvalid(L, "cupheader_t"); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	switch (field) | ||||
| 	{ | ||||
| 	case cup_valid: | ||||
| 		lua_pushboolean(L, true); | ||||
| 		break; | ||||
| 	case cup_id: | ||||
| 		lua_pushnumber(L, cup->id); | ||||
| 		break; | ||||
| 	case cup_monitor: | ||||
| 		lua_pushnumber(L, cup->monitor); | ||||
| 		break; | ||||
| 	case cup_name: | ||||
| 		lua_pushstring(L, cup->name); | ||||
| 		break; | ||||
| 	case cup_namehash: | ||||
| 		return UNIMPLEMENTED; | ||||
| 		break; | ||||
| 	case cup_realname: | ||||
| 		lua_pushstring(L, cup->realname); | ||||
| 		break; | ||||
| 	case cup_icon: | ||||
| 		lua_pushstring(L, cup->icon); | ||||
| 		break; | ||||
| 	case cup_levellist: | ||||
| 		lua_createtable(L, ((cup->numlevels) + (cup->numbonus)), 0); | ||||
| 		for (size_t i = 0; i < ((cup->numlevels) + (cup->numbonus)); i++) | ||||
| 		{ | ||||
| 			lua_pushstring(L, cup->levellist[i]); | ||||
| 			lua_rawseti(L, -2, 1 + i); | ||||
| 		} | ||||
| 		break; | ||||
| 	case cup_cachedlevels: | ||||
| 		lua_createtable(L, ((cup->numlevels) + (cup->numbonus)), 0); | ||||
| 		for (size_t i = 0; i < CUPCACHE_MAX; i++) | ||||
| 		{ | ||||
| 			lua_pushnumber(L, (cup->cachedlevels[i])+1); | ||||
| 			lua_rawseti(L, -2, 1 + i); | ||||
| 		} | ||||
| 		break; | ||||
| 	case cup_numlevels: | ||||
| 		lua_pushnumber(L, cup->numlevels); | ||||
| 		break; | ||||
| 	case cup_numbonus: | ||||
| 		lua_pushnumber(L, cup->numbonus); | ||||
| 		break; | ||||
| 	case cup_emeraldnum: | ||||
| 		lua_pushnumber(L, cup->emeraldnum); | ||||
| 		break; | ||||
| 	case cup_playcredits: | ||||
| 		lua_pushboolean(L, cup->playcredits); | ||||
| 		break; | ||||
| 	case cup_hintcondition: | ||||
| 		lua_pushnumber(L, cup->hintcondition); | ||||
| 		break; | ||||
| 	case cup_cache_cuplock: | ||||
| 		return UNIMPLEMENTED; | ||||
| 		break; | ||||
| 	case cup_windata: | ||||
| 		return UNIMPLEMENTED; | ||||
| 		break; | ||||
| 	case cup_next: | ||||
| 		return UNIMPLEMENTED; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return RNOFIELDCH; | ||||
| 	} | ||||
| 	 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int cup_set(lua_State *L) | ||||
| { | ||||
| 	return luaL_error(L, LUA_QL("cupheader_t") " struct cannot be edited by Lua."); | ||||
| } | ||||
| 
 | ||||
| static int gprank_get(lua_State *L) | ||||
| { | ||||
| 	gpRank_t *gprank = *((gpRank_t **)luaL_checkudata(L, 1, META_GPRANK)); | ||||
| 	enum gprank field = luaL_checkoption(L, 2, gprank_opt[0], gprank_opt); | ||||
| 	 | ||||
| 	if (!gprank) | ||||
| 	{ | ||||
| 		switch (field) | ||||
| 		{ | ||||
| 			case gprank_valid: | ||||
| 				lua_pushboolean(L, false); | ||||
| 				return 1; | ||||
| 			default: | ||||
| 				return LUA_ErrInvalid(L, "gprank_t"); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	switch (field) | ||||
| 	{ | ||||
| 	case gprank_valid: | ||||
| 		lua_pushboolean(L, true); | ||||
| 		break; | ||||
| 	case gprank_numplayers: | ||||
| 		lua_pushnumber(L, gprank->numPlayers); | ||||
| 		break; | ||||
| 	case gprank_totalplayers: | ||||
| 		lua_pushnumber(L, gprank->totalPlayers); | ||||
| 		break; | ||||
| 	case gprank_position: | ||||
| 		lua_pushnumber(L, gprank->position); | ||||
| 		break; | ||||
| 	case gprank_skin: | ||||
| 		lua_pushnumber(L, gprank->skin); | ||||
| 		break; | ||||
| 	case gprank_winpoints: | ||||
| 		lua_pushnumber(L, gprank->winPoints); | ||||
| 		break; | ||||
| 	case gprank_totalpoints: | ||||
| 		lua_pushnumber(L, gprank->totalPoints); | ||||
| 		break; | ||||
| 	case gprank_exp: | ||||
| 		lua_pushnumber(L, gprank->exp); | ||||
| 		break; | ||||
| 	case gprank_totalexp: | ||||
| 		lua_pushnumber(L, gprank->totalExp); | ||||
| 		break; | ||||
| 	case gprank_continuesused: | ||||
| 		lua_pushnumber(L, gprank->continuesUsed); | ||||
| 		break; | ||||
| 	case gprank_prisons: | ||||
| 		lua_pushnumber(L, gprank->prisons); | ||||
| 		break; | ||||
| 	case gprank_totalprisons: | ||||
| 		lua_pushnumber(L, gprank->totalPrisons); | ||||
| 		break; | ||||
| 	case gprank_rings: | ||||
| 		lua_pushnumber(L, gprank->rings); | ||||
| 		break; | ||||
| 	case gprank_totalrings: | ||||
| 		lua_pushnumber(L, gprank->totalRings); | ||||
| 		break; | ||||
| 	case gprank_specialwon: | ||||
| 		lua_pushboolean(L, gprank->specialWon); | ||||
| 		break; | ||||
| 	case gprank_scoreposition: | ||||
| 		lua_pushnumber(L, gprank->scorePosition); | ||||
| 		break; | ||||
| 	case gprank_scoregppoints: | ||||
| 		lua_pushnumber(L, gprank->scoreGPPoints); | ||||
| 		break; | ||||
| 	case gprank_scoreexp: | ||||
| 		lua_pushnumber(L, gprank->scoreExp); | ||||
| 		break; | ||||
| 	case gprank_scoreprisons: | ||||
| 		lua_pushnumber(L, gprank->scorePrisons); | ||||
| 		break; | ||||
| 	case gprank_scorerings: | ||||
| 		lua_pushnumber(L, gprank->scoreRings); | ||||
| 		break; | ||||
| 	case gprank_scorecontinues: | ||||
| 		lua_pushnumber(L, gprank->scoreContinues); | ||||
| 		break; | ||||
| 	case gprank_scoretotal: | ||||
| 		lua_pushnumber(L, gprank->scoreTotal); | ||||
| 		break; | ||||
| 	case gprank_numlevels: | ||||
| 		lua_pushnumber(L, gprank->numLevels); | ||||
| 		break; | ||||
| 	case gprank_levels: | ||||
| 		lua_createtable(L, ((grandprixinfo.cup->numlevels) + (grandprixinfo.cup->numbonus)), 0); | ||||
| 		for (size_t i = 0; i < ((grandprixinfo.cup->numlevels) + (grandprixinfo.cup->numbonus)); i++) | ||||
| 		{ | ||||
| 			LUA_PushUserdata(L, &gprank->levels[i], META_GPRANKLEVEL); | ||||
| 			lua_rawseti(L, -2, 1 + i); | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		return RNOFIELDGR; | ||||
| 	} | ||||
| 	 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int gprank_set(lua_State *L) | ||||
| { | ||||
| 	return luaL_error(L, LUA_QL("gprank_t") " struct cannot be edited by Lua."); | ||||
| } | ||||
| 
 | ||||
| static int gprank_level_get(lua_State *L) | ||||
| { | ||||
| 	gpRank_level_t *gprank_level = *((gpRank_level_t **)luaL_checkudata(L, 1, META_GPRANKLEVEL)); | ||||
| 	enum gprank_level field = luaL_checkoption(L, 2, gprank_level_opt[0], gprank_level_opt); | ||||
| 	 | ||||
| 	if (!gprank_level) | ||||
| 	{ | ||||
| 		switch (field) | ||||
| 		{ | ||||
| 			case gprank_level_valid: | ||||
| 				lua_pushboolean(L, false); | ||||
| 				return 1; | ||||
| 			default: | ||||
| 				return LUA_ErrInvalid(L, "gprank_level_t"); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	switch (field) | ||||
| 	{ | ||||
| 	case gprank_level_valid: | ||||
| 		lua_pushboolean(L, true); | ||||
| 		break; | ||||
| 	case gprank_level_id: | ||||
| 		lua_pushnumber(L, gprank_level->id); | ||||
| 		break; | ||||
| 	case gprank_level_event: | ||||
| 		lua_pushnumber(L, gprank_level->event); | ||||
| 		break; | ||||
| 	case gprank_level_time: | ||||
| 		lua_pushnumber(L, gprank_level->time); | ||||
| 		break; | ||||
| 	case gprank_level_totalexp: | ||||
| 		lua_pushnumber(L, gprank_level->totalExp); | ||||
| 		break; | ||||
| 	case gprank_level_totalprisons: | ||||
| 		lua_pushnumber(L, gprank_level->totalPrisons); | ||||
| 		break; | ||||
| 	case gprank_level_continues: | ||||
| 		lua_pushnumber(L, gprank_level->continues); | ||||
| 		break; | ||||
| 	case gprank_level_perplayer: | ||||
| 		lua_createtable(L, grandprixinfo.rank.numPlayers, 0); | ||||
| 		for (size_t i = 0; i < grandprixinfo.rank.numPlayers; i++) | ||||
| 		{ | ||||
| 			LUA_PushUserdata(L, &gprank_level->perPlayer[i], META_GPRANKLEVELPERPLAYER); | ||||
| 			lua_rawseti(L, -2, 1 + i); | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		return RNOFIELDGRL; | ||||
| 	} | ||||
| 	 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int gprank_level_set(lua_State *L) | ||||
| { | ||||
| 	return luaL_error(L, LUA_QL("gprank_level_t") " struct cannot be edited by Lua."); | ||||
| } | ||||
| 
 | ||||
| static int gprank_level_perplayer_get(lua_State *L) | ||||
| { | ||||
| 	// "perplaya" to avoid shadowed declaration
 | ||||
| 	gpRank_level_perplayer_t *gprank_level_perplaya = *((gpRank_level_perplayer_t **)luaL_checkudata(L, 1, META_GPRANKLEVELPERPLAYER)); | ||||
| 	enum gprank_level_perplayer field = luaL_checkoption(L, 2, gprank_level_perplayer_opt[0], gprank_level_perplayer_opt); | ||||
| 	 | ||||
| 	if (!gprank_level_perplaya) | ||||
| 	{ | ||||
| 		switch (field) | ||||
| 		{ | ||||
| 			case gprank_level_perplayer_valid: | ||||
| 				lua_pushboolean(L, false); | ||||
| 				return 1; | ||||
| 			default: | ||||
| 				return LUA_ErrInvalid(L, "gprank_level_perplayer_t"); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	switch (field) | ||||
| 	{ | ||||
| 	case gprank_level_perplayer_valid: | ||||
| 		lua_pushboolean(L, true); | ||||
| 		break; | ||||
| 	case gprank_level_perplayer_position: | ||||
| 		lua_pushnumber(L, gprank_level_perplaya->position); | ||||
| 		break; | ||||
| 	case gprank_level_perplayer_rings: | ||||
| 		lua_pushnumber(L, gprank_level_perplaya->rings); | ||||
| 		break; | ||||
| 	case gprank_level_perplayer_exp: | ||||
| 		lua_pushnumber(L, gprank_level_perplaya->exp); | ||||
| 		break; | ||||
| 	case gprank_level_perplayer_prisons: | ||||
| 		lua_pushnumber(L, gprank_level_perplaya->prisons); | ||||
| 		break; | ||||
| 	case gprank_level_perplayer_gotspecialprize: | ||||
| 		lua_pushboolean(L, gprank_level_perplaya->gotSpecialPrize); | ||||
| 		break; | ||||
| 	case gprank_level_perplayer_grade: | ||||
| 		lua_pushnumber(L, gprank_level_perplaya->grade); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return RNOFIELDGRLP; | ||||
| 	} | ||||
| 	 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int gprank_level_perplayer_set(lua_State *L) | ||||
| { | ||||
| 	return luaL_error(L, LUA_QL("gprank_level_perplayer") " struct cannot be edited by Lua."); | ||||
| } | ||||
| 
 | ||||
| static int roundcue_get(lua_State *L) | ||||
| { | ||||
| 	enum roundcue field = luaL_checkoption(L, 2, roundcue_opt[0], roundcue_opt); | ||||
| 	 | ||||
| 	// Don't return any grandprixinfo values while not in a GP.
 | ||||
| 	if (!roundqueue.size) | ||||
| 	{ | ||||
| 		switch (field) | ||||
| 		{ | ||||
| 			case roundcue_size: | ||||
| 				lua_pushboolean(L, false); | ||||
| 				return 1; | ||||
| 			default: | ||||
| 				return ROUNDCUEERR; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	switch (field) | ||||
| 	{ | ||||
| 	case roundcue_size: | ||||
| 		lua_pushnumber(L, roundqueue.size); | ||||
| 		break; | ||||
| 	case roundcue_roundnum: | ||||
| 		lua_pushnumber(L, roundqueue.roundnum); | ||||
| 		break; | ||||
| 	case roundcue_position: | ||||
| 		lua_pushnumber(L, roundqueue.position); | ||||
| 		break; | ||||
| 	case roundcue_netcommunicate: | ||||
| 		return UNIMPLEMENTED; | ||||
| 		break; | ||||
| 	case roundcue_writetextmap: | ||||
| 		return UNIMPLEMENTED; | ||||
| 		break; | ||||
| 	case roundcue_snapshotmaps: | ||||
| 		lua_pushboolean(L, roundqueue.snapshotmaps); | ||||
| 		break; | ||||
| 	case roundcue_entries: | ||||
| 		lua_createtable(L, roundqueue.size, 0); | ||||
| 		for (size_t i = 0; i < roundqueue.size; i++) | ||||
| 		{ | ||||
| 			LUA_PushUserdata(L, &roundqueue.entries[i], META_ROUNDENTRY); | ||||
| 			lua_rawseti(L, -2, 1 + i); | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		return RNOFIELDRQ; | ||||
| 	} | ||||
| 	 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int roundcue_set(lua_State *L) | ||||
| { | ||||
| 	return luaL_error(L, LUA_QL("roundqueue") " struct cannot be edited by Lua."); | ||||
| } | ||||
| 
 | ||||
| static int roundentry_get(lua_State *L) | ||||
| { | ||||
| 	roundentry_t *roundentry = *((roundentry_t **)luaL_checkudata(L, 1, META_ROUNDENTRY)); | ||||
| 	enum roundentry field = luaL_checkoption(L, 2, roundentry_opt[0], roundentry_opt); | ||||
| 	 | ||||
| 	if (!roundentry) | ||||
| 	{ | ||||
| 		switch (field) | ||||
| 		{ | ||||
| 			case roundentry_valid: | ||||
| 				lua_pushboolean(L, false); | ||||
| 				return 1; | ||||
| 			default: | ||||
| 				return LUA_ErrInvalid(L, "roundentry_t"); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	switch (field) | ||||
| 	{ | ||||
| 	case roundentry_valid: | ||||
| 		lua_pushboolean(L, true); | ||||
| 		break; | ||||
| 	case roundentry_mapnum: | ||||
| 		lua_pushnumber(L, roundentry->mapnum); | ||||
| 		break; | ||||
| 	case roundentry_gametype: | ||||
| 		lua_pushnumber(L, roundentry->gametype); | ||||
| 		break; | ||||
| 	case roundentry_encore: | ||||
| 		lua_pushboolean(L, roundentry->encore); | ||||
| 		break; | ||||
| 	case roundentry_rankrestricted: | ||||
| 		lua_pushboolean(L, roundentry->rankrestricted); | ||||
| 		break; | ||||
| 	case roundentry_overridden: | ||||
| 		lua_pushboolean(L, roundentry->overridden); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return RNOFIELDRE; | ||||
| 	} | ||||
| 	 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int roundentry_set(lua_State *L) | ||||
| { | ||||
| 	return luaL_error(L, LUA_QL("roundentry_t") " struct cannot be edited by Lua."); | ||||
| } | ||||
| 
 | ||||
| #undef UNIMPLEMENTED  | ||||
| #undef RNOFIELDGP  | ||||
| #undef RNOFIELDCH  | ||||
| #undef RNOFIELDGR  | ||||
| #undef RNOFIELDGRL  | ||||
| #undef RNOFIELDGRLP  | ||||
| #undef RNOFIELDRQ  | ||||
| #undef RNOFIELDRE  | ||||
| #undef GPERR | ||||
| #undef ROUNDCUEERR | ||||
| 
 | ||||
| static int lib_numCupheaders(lua_State *L) | ||||
| { | ||||
| 	lua_pushinteger(L, numkartcupheaders); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| // There was, in fact, a better thing to do here - thanks toaster
 | ||||
| #define GETCUPERR UINT16_MAX | ||||
| 
 | ||||
| // copied and edited from G_MapNumber
 | ||||
| static UINT16 LUA_GetCupByNum(UINT16 cupnum) | ||||
| { | ||||
| 	cupheader_t *checkcup; | ||||
| 	// find by cup id
 | ||||
| 	if (cupnum != GETCUPERR) | ||||
| 	{ | ||||
| 		if (cupnum >= numkartcupheaders) | ||||
| 			return GETCUPERR; // id outta range
 | ||||
| 		for (checkcup = kartcupheaders; checkcup->id <= numkartcupheaders; checkcup = checkcup->next) | ||||
| 		{ | ||||
| 			if (checkcup->id != cupnum) | ||||
| 				continue; | ||||
| 			else | ||||
| 				break; | ||||
| 			return GETCUPERR; // id invalid
 | ||||
| 		} | ||||
| 		return checkcup->id; | ||||
| 	} | ||||
| 
 | ||||
| 	return GETCUPERR; | ||||
| } | ||||
| 
 | ||||
| // copied and edited from G_MapNumber
 | ||||
| static UINT16 LUA_GetCupByName(const char * name) | ||||
| { | ||||
| 	cupheader_t *checkcup; | ||||
| 	 | ||||
| 	UINT32 hash = quickncasehash(name, MAXCUPNAME); | ||||
| 
 | ||||
| 	// find by cup name/realname
 | ||||
| 	for (checkcup = kartcupheaders; checkcup != NULL; checkcup = checkcup->next) | ||||
| 	{ | ||||
| 		if (hash != checkcup->namehash) | ||||
| 			continue; | ||||
| 		 | ||||
| 		if (strcasecmp(checkcup->name, name) != 0) | ||||
| 			continue; | ||||
| 
 | ||||
| 		return checkcup->id; | ||||
| 	} | ||||
| 
 | ||||
| 	return GETCUPERR; | ||||
| } | ||||
| 
 | ||||
| static int lib_iterateCups(lua_State *L) | ||||
| { | ||||
| 	INT32 i = -1; | ||||
| 	cupheader_t *tempcup = kartcupheaders; | ||||
| 
 | ||||
| 	if (lua_gettop(L) < 2) | ||||
| 	{ | ||||
| 		lua_pushcfunction(L, lib_iterateCups); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	lua_settop(L, 2); | ||||
| 	lua_remove(L, 1); // state is unused.
 | ||||
| 
 | ||||
| 	if (!lua_isnil(L, 1)) | ||||
| 	{ | ||||
| 		i = ((*((cupheader_t **)luaL_checkudata(L, 1, META_CUP)))->id) + 1; | ||||
| 	} | ||||
| 	else | ||||
| 		i = 0; | ||||
| 	 | ||||
| 	for (tempcup = kartcupheaders; tempcup->id < numkartcupheaders; tempcup = tempcup->next) | ||||
| 	{ | ||||
| 		if (tempcup->next == NULL) | ||||
| 			break; | ||||
| 		 | ||||
| 		if (tempcup->id >= i) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	// cups are always valid, only added, never removed
 | ||||
| 	if (i < numkartcupheaders) | ||||
| 	{ | ||||
| 		LUA_PushUserdata(L, tempcup, META_CUP); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| // Shamelessly copied and edited from lua_waypointslib.c (with thanks to JugadorXEI)
 | ||||
| static int lib_getCupheader(lua_State *L) | ||||
| { | ||||
| 	const char *field; | ||||
| 	size_t i; | ||||
| 	cupheader_t *checkcup; | ||||
| 	UINT16 getResult = GETCUPERR; | ||||
| 	 | ||||
| 	// find cup by id number
 | ||||
| 	if (lua_type(L, 2) == LUA_TNUMBER) | ||||
| 	{ | ||||
| 		i = luaL_checkinteger(L, 2); | ||||
| 		if (i > numkartcupheaders) | ||||
| 			return luaL_error(L, "cupheader_t id %d out of loaded range (0 - %d)", i, numkartcupheaders); | ||||
| 		getResult = LUA_GetCupByNum(i); | ||||
| 		if (getResult == GETCUPERR) | ||||
| 			return luaL_error(L, "cupheader_t id %d invalid", i); | ||||
| 		for (checkcup = kartcupheaders; checkcup->id < numkartcupheaders; checkcup = checkcup->next) | ||||
| 		{ | ||||
| 			if (checkcup->id != getResult) | ||||
| 				continue; | ||||
| 			else | ||||
| 				break; | ||||
| 			 | ||||
| 			return luaL_error(L, "cupheader_t id %d invalid (LUA_GetCupByNum failed?)", i); | ||||
| 		} | ||||
| 		LUA_PushUserdata(L, checkcup, META_CUP); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	 | ||||
| 	field = luaL_checkstring(L, 2); | ||||
| 	 | ||||
| 	// special function iterate
 | ||||
| 	if (fastcmp(field,"iterate")) | ||||
| 	{ | ||||
| 		lua_pushcfunction(L, lib_iterateCups); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	 | ||||
| 	if (lua_type(L, 2) == LUA_TSTRING) | ||||
| 	{ | ||||
| 		getResult = LUA_GetCupByName(field); | ||||
| 		if (getResult == GETCUPERR) | ||||
| 			return luaL_error(L, "no cupheader_t with name %s", field); | ||||
| 	} | ||||
| 	 | ||||
| 	// If, after all this...
 | ||||
| 	if (getResult == GETCUPERR) | ||||
| 		return luaL_error(L, "internal failure in lua_grandprixlib.c???"); | ||||
| 	 | ||||
| 	for (checkcup = kartcupheaders; checkcup->id < numkartcupheaders; checkcup = checkcup->next) | ||||
| 	{ | ||||
| 		if (checkcup->id != getResult) | ||||
| 			continue; | ||||
| 		else | ||||
| 			break; | ||||
| 		 | ||||
| 		return luaL_error(L, "cupheader_t id %d invalid (LUA_GetCupByName failed?)", i); | ||||
| 	} | ||||
| 	 | ||||
| 	LUA_PushUserdata(L, checkcup, META_CUP); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| int LUA_GrandPrixLib(lua_State *L) | ||||
| { | ||||
| 	lua_newuserdata(L, 0); | ||||
| 		lua_createtable(L, 0, 2); | ||||
| 			lua_pushcfunction(L, grandprix_get); | ||||
| 			lua_setfield(L, -2, "__index"); | ||||
| 
 | ||||
| 			lua_pushcfunction(L, grandprix_set); | ||||
| 			lua_setfield(L, -2, "__newindex"); | ||||
| 		lua_setmetatable(L, -2); | ||||
| 	lua_setglobal(L, "grandprixinfo"); | ||||
| 	 | ||||
| 	lua_newuserdata(L, 0); | ||||
| 		lua_createtable(L, 0, 2); | ||||
| 			lua_pushcfunction(L, roundcue_get); | ||||
| 			lua_setfield(L, -2, "__index"); | ||||
| 
 | ||||
| 			lua_pushcfunction(L, roundcue_set); | ||||
| 			lua_setfield(L, -2, "__newindex"); | ||||
| 		lua_setmetatable(L, -2); | ||||
| 	lua_setglobal(L, "roundqueue"); | ||||
| 
 | ||||
| 	luaL_newmetatable(L, META_CUP); | ||||
| 		lua_pushcfunction(L, cup_get); | ||||
| 		lua_setfield(L, -2, "__index"); | ||||
| 
 | ||||
| 		lua_pushcfunction(L, cup_set); | ||||
| 		lua_setfield(L, -2, "__newindex"); | ||||
| 	lua_pop(L,1); | ||||
| 	 | ||||
| 	luaL_newmetatable(L, META_GPRANK); | ||||
| 		lua_pushcfunction(L, gprank_get); | ||||
| 		lua_setfield(L, -2, "__index"); | ||||
| 
 | ||||
| 		lua_pushcfunction(L, gprank_set); | ||||
| 		lua_setfield(L, -2, "__newindex"); | ||||
| 	lua_pop(L,1); | ||||
| 	 | ||||
| 	luaL_newmetatable(L, META_GPRANKLEVEL); | ||||
| 		lua_pushcfunction(L, gprank_level_get); | ||||
| 		lua_setfield(L, -2, "__index"); | ||||
| 
 | ||||
| 		lua_pushcfunction(L, gprank_level_set); | ||||
| 		lua_setfield(L, -2, "__newindex"); | ||||
| 	lua_pop(L,1); | ||||
| 	 | ||||
| 	luaL_newmetatable(L, META_GPRANKLEVELPERPLAYER); | ||||
| 		lua_pushcfunction(L, gprank_level_perplayer_get); | ||||
| 		lua_setfield(L, -2, "__index"); | ||||
| 
 | ||||
| 		lua_pushcfunction(L, gprank_level_perplayer_set); | ||||
| 		lua_setfield(L, -2, "__newindex"); | ||||
| 	lua_pop(L,1); | ||||
| 	 | ||||
| 	luaL_newmetatable(L, META_ROUNDENTRY); | ||||
| 		lua_pushcfunction(L, roundentry_get); | ||||
| 		lua_setfield(L, -2, "__index"); | ||||
| 
 | ||||
| 		lua_pushcfunction(L, roundentry_set); | ||||
| 		lua_setfield(L, -2, "__newindex"); | ||||
| 	lua_pop(L,1); | ||||
| 	 | ||||
| 	lua_newuserdata(L, 0); | ||||
| 		lua_createtable(L, 0, 2); | ||||
| 			lua_pushcfunction(L, lib_getCupheader); | ||||
| 			lua_setfield(L, -2, "__index"); | ||||
| 
 | ||||
| 			lua_pushcfunction(L, lib_numCupheaders); | ||||
| 			lua_setfield(L, -2, "__len"); | ||||
| 		lua_setmetatable(L, -2); | ||||
| 	lua_setglobal(L, "cups"); | ||||
| 	 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -122,6 +122,12 @@ extern lua_State *gL; | |||
| #define META_ICECUBEVARS "ICECUBEVARS_T*" | ||||
| #define META_SKYBOX "SKYBOX_T*" | ||||
| 
 | ||||
| #define META_CUP "CUPHEADER_T*" | ||||
| #define META_GPRANK "GPRANK_T*" | ||||
| #define META_GPRANKLEVEL "GPRANK_LEVEL_T*" | ||||
| #define META_GPRANKLEVELPERPLAYER "GPRANK_LEVEL_PERPLAYER_T*" | ||||
| #define META_ROUNDENTRY "ROUNDENTRY_T*" | ||||
| 
 | ||||
| boolean luaL_checkboolean(lua_State *L, int narg); | ||||
| 
 | ||||
| int LUA_EnumLib(lua_State *L); | ||||
|  | @ -146,6 +152,7 @@ int LUA_BotVarsLib(lua_State *L); | |||
| int LUA_TerrainLib(lua_State *L); | ||||
| int LUA_RespawnVarsLib(lua_State *L); | ||||
| int LUA_WaypointLib(lua_State *L); | ||||
| int LUA_GrandPrixLib(lua_State *L); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } // extern "C"
 | ||||
|  |  | |||
|  | @ -215,6 +215,8 @@ static int player_get(lua_State *L) | |||
| 		lua_pushinteger(L, plr->oldposition); | ||||
| 	else if (fastcmp(field,"positiondelay")) | ||||
| 		lua_pushinteger(L, plr->positiondelay); | ||||
| 	else if (fastcmp(field,"leaderpenalty")) | ||||
| 		lua_pushinteger(L, plr->leaderpenalty); | ||||
| 	else if (fastcmp(field,"teamposition")) | ||||
| 		lua_pushinteger(L, plr->teamposition); | ||||
| 	else if (fastcmp(field,"teamimportance")) | ||||
|  | @ -255,6 +257,8 @@ static int player_get(lua_State *L) | |||
| 		lua_pushinteger(L, plr->justbumped); | ||||
| 	else if (fastcmp(field,"noebrakemagnet")) | ||||
| 		lua_pushinteger(L, plr->noEbrakeMagnet); | ||||
| 	else if (fastcmp(field,"wallspikedampen")) | ||||
| 		lua_pushinteger(L, plr->wallSpikeDampen); | ||||
| 	else if (fastcmp(field,"tumblebounces")) | ||||
| 		lua_pushinteger(L, plr->tumbleBounces); | ||||
| 	else if (fastcmp(field,"tumbleheight")) | ||||
|  | @ -988,6 +992,8 @@ static int player_set(lua_State *L) | |||
| 		plr->oldposition = luaL_checkinteger(L, 3); | ||||
| 	else if (fastcmp(field,"positiondelay")) | ||||
| 		plr->positiondelay = luaL_checkinteger(L, 3); | ||||
| 	else if (fastcmp(field,"leaderpenalty")) | ||||
| 		plr->leaderpenalty = luaL_checkinteger(L, 3); | ||||
| 	else if (fastcmp(field,"teamposition")) | ||||
| 		plr->teamposition = luaL_checkinteger(L, 3); | ||||
| 	else if (fastcmp(field,"teamimportance")) | ||||
|  | @ -1033,6 +1039,8 @@ static int player_set(lua_State *L) | |||
| 		plr->justbumped = luaL_checkinteger(L, 3); | ||||
| 	else if (fastcmp(field,"noebrakemagnet")) | ||||
| 		plr->noEbrakeMagnet = luaL_checkinteger(L, 3); | ||||
| 	else if (fastcmp(field,"wallspikedampen")) | ||||
| 		plr->wallSpikeDampen = luaL_checkinteger(L, 3); | ||||
| 	else if (fastcmp(field,"tumblebounces")) | ||||
| 		plr->tumbleBounces = luaL_checkinteger(L, 3); | ||||
| 	else if (fastcmp(field,"tumbleheight")) | ||||
|  |  | |||
|  | @ -66,6 +66,7 @@ static lua_CFunction liblist[] = { | |||
| 	LUA_TerrainLib, // t_splash_t, t_footstep_t, t_overlay_t, terrain_t
 | ||||
| 	LUA_RespawnVarsLib, // respawnvars_t
 | ||||
| 	LUA_WaypointLib, // waypoint_t
 | ||||
| 	LUA_GrandPrixLib, // grandprixinfo, cupheader_t, gprank_t, skinrecord_t, etc.
 | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -117,6 +117,9 @@ void COM_Lua_f(void); | |||
| // Music: "No tune" error.
 | ||||
| #define LUA_ErrNoTune(L, tune) luaL_error(L, "tune \"%s\" does not exist", tune) | ||||
| 
 | ||||
| // Music: "Stereo Mode" error.
 | ||||
| #define LUA_ErrStereo(L, tune) luaL_error(L, "tune \"%s\" cannot be remapped (stereo mode)", tune) | ||||
| 
 | ||||
| // Deprecation warnings
 | ||||
| // Shows once upon use. Then doesn't show again.
 | ||||
| #define LUA_Deprecated(L,this_func,use_instead)\ | ||||
|  |  | |||
|  | @ -229,7 +229,7 @@ EggTVData::Replay::Replay(Folder::Cache::ReplayRef& ref) : ref_(&ref) | |||
| 		const std::string_view str = info.title; | ||||
| 		const std::size_t mid = str.find(kDelimiter); | ||||
| 
 | ||||
| 		title_ = Title(str.substr(0, mid), mid == srb2::String::npos ? "" : str.substr(mid + kDelimiter.size())); | ||||
| 		title_ = Title(str.substr(0, mid), mid == std::string_view::npos ? "" : str.substr(mid + kDelimiter.size())); | ||||
| 		//title_ = Title("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", "WWWWWWWWWWWWWWWWWWWWWWWWWWW");
 | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -57,12 +57,12 @@ static void M_GonerHandStrain(INT32 ch) | |||
| 
 | ||||
| static void M_GonerPhotosensitivity(INT32 ch) | ||||
| { | ||||
| 	if (ch == MA_YES) | ||||
| 	{ | ||||
| 		CV_StealthSet(&cv_reducevfx, "Yes"); | ||||
| 		CV_StealthSet(&cv_screenshake, "Off"); | ||||
| 		CV_StealthSet(&cv_tilting, "Off"); | ||||
| 	} | ||||
| 	// if (ch == MA_YES)
 | ||||
| 	// {
 | ||||
| 	// 	CV_StealthSet(&cv_reducevfx, "Yes");
 | ||||
| 	// 	CV_StealthSet(&cv_screenshake, "Off");
 | ||||
| 	// 	CV_StealthSet(&cv_tilting, "Off");
 | ||||
| 	// }
 | ||||
| 
 | ||||
| #ifdef HANDSTRAIN | ||||
| 	M_StartMessage("Hand strain warning", | ||||
|  | @ -96,9 +96,12 @@ static void M_GonerAccessibilityTick(void) | |||
| 		"patterns.""\x80"" Listen to your body, and\n" | ||||
| 		"stop playing if you feel unwell.\n" | ||||
| 		"\n" | ||||
| 		"There is a ""\x88""special mode""\x80"" to reduce some\n" | ||||
| 		"visual effects. Would you like to turn it on?\n" | ||||
| 		, &M_GonerPhotosensitivity, MM_YESNO, "Yes, reduce effects", "No thanks"); | ||||
| 		"There is a ""\x88""special mode""\x80""\n" | ||||
| 		"to reduce some visual effects.\n" | ||||
| 		"\n" | ||||
| 		"You can turn it on within the\n" | ||||
| 		"Profile Setup > Accessibility menu.\n" | ||||
| 		, &M_GonerPhotosensitivity, MM_NOTHING, NULL, NULL); | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ menuitem_t OPTIONS_DataAdvancedAddon[] = | |||
| 	{IT_STRING | IT_CVAR, "Matching", "Set where to check for the text pattern when looking up addons via name.", | ||||
| 		NULL, {.cvar = &cv_addons_search_type}, 0, 0}, | ||||
| 
 | ||||
| 	{IT_STRING | IT_CVAR, "Case Sensitivity", "Set whether to consider the case when searching for addons..", | ||||
| 	{IT_STRING | IT_CVAR, "Case Sensitivity", "Set whether to consider the case when searching for addons.", | ||||
| 		NULL, {.cvar = &cv_addons_search_case}, 0, 0}, | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -1019,7 +1019,10 @@ static void M_HandleColorRotate(setup_player_t *p, UINT8 num) | |||
| 	{ | ||||
| 		if (p->skin >= 0) | ||||
| 		{ | ||||
| 			p->color = SKINCOLOR_NONE; | ||||
| 			if (p->color == SKINCOLOR_NONE) | ||||
| 				p->color = PR_GetProfile(p->profilen)->color; | ||||
| 			else | ||||
| 				p->color = SKINCOLOR_NONE; | ||||
| 			p->rotate = CSROTATETICS; | ||||
| 			p->hitlag = true; | ||||
| 			S_StartSound(NULL, sfx_s3k7b); //sfx_s3kc3s
 | ||||
|  | @ -1252,8 +1255,11 @@ static void M_HandleFollowerColorRotate(setup_player_t *p, UINT8 num) | |||
| 	} | ||||
| 	else if (M_MenuExtraPressed(num)) | ||||
| 	{ | ||||
| 		UINT16 profile_followercolor = PR_GetProfile(p->profilen)->followercolor; | ||||
| 		if (p->followercolor == FOLLOWERCOLOR_MATCH) | ||||
| 			p->followercolor = FOLLOWERCOLOR_OPPOSITE; | ||||
| 		else if (p->followercolor == FOLLOWERCOLOR_OPPOSITE && profile_followercolor != FOLLOWERCOLOR_OPPOSITE && profile_followercolor != FOLLOWERCOLOR_MATCH) | ||||
| 			p->followercolor = profile_followercolor; | ||||
| 		else if (p->followercolor == SKINCOLOR_NONE) | ||||
| 			p->followercolor = FOLLOWERCOLOR_MATCH; | ||||
| 		else | ||||
|  |  | |||
|  | @ -266,6 +266,23 @@ menu_t PLAY_TAGhostsDef = { | |||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| // See also G_UpdateRecordReplays
 | ||||
| const char *M_GetRecordMode(void) | ||||
| { | ||||
| 	if (cv_dummyspbattack.value) | ||||
| 	{ | ||||
| 		return "spb-"; | ||||
| 	} | ||||
| 
 | ||||
| 	const INT32 skinid = R_SkinAvailableEx(cv_skin[0].string, false); | ||||
| 	if (skinid >= 0 && (skins[skinid]->flags & SF_HIVOLT)) | ||||
| 	{ | ||||
| 		return "hivolt-"; | ||||
| 	} | ||||
| 
 | ||||
| 	return ""; | ||||
| } | ||||
| 
 | ||||
| void CV_SPBAttackChanged(void); | ||||
| void CV_SPBAttackChanged(void) | ||||
| { | ||||
|  | @ -275,14 +292,12 @@ void CV_SPBAttackChanged(void) | |||
| 	{ | ||||
| 		// see also p_setup.c's P_LoadRecordGhosts
 | ||||
| 		char *gpath = Z_StrDup(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(levellist.choosemap+1))); | ||||
| 		const char *modeprefix = ""; | ||||
| 		const char *modeprefix = M_GetRecordMode(); | ||||
| 		UINT8 active; | ||||
| 
 | ||||
| 		if (!gpath) | ||||
| 			return; | ||||
| 
 | ||||
| 		if (cv_dummyspbattack.value) | ||||
| 			modeprefix = "spb-"; | ||||
| 
 | ||||
| 		active = false; | ||||
| 		PLAY_TimeAttack[ta_guest].status = IT_DISABLED; | ||||
|  | @ -458,20 +473,7 @@ void M_ReplayTimeAttack(INT32 choice) | |||
| { | ||||
| 	menudemo_t menudemo = {0}; | ||||
| 	const char *which = NULL; | ||||
| 	const char *modeprefix = ""; | ||||
| 
 | ||||
| 	if (cv_dummyspbattack.value) | ||||
| 	{ | ||||
| 		modeprefix = "spb-"; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		const INT32 skinid = R_SkinAvailableEx(cv_skin[0].string, false); | ||||
| 		if (skinid >= 0 && (skins[skinid]->flags & SF_HIVOLT)) | ||||
| 		{ | ||||
| 			modeprefix = "hivolt-"; | ||||
| 		} | ||||
| 	} | ||||
| 	const char *modeprefix = M_GetRecordMode(); | ||||
| 
 | ||||
| 	switch (choice) | ||||
| 	{ | ||||
|  | @ -528,20 +530,7 @@ static void M_WriteGuestReplay(INT32 ch) | |||
| 
 | ||||
| 	gpath = Z_StrDup(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(levellist.choosemap+1))); | ||||
| 
 | ||||
| 	const char *modeprefix = ""; | ||||
| 
 | ||||
| 	if (cv_dummyspbattack.value) | ||||
| 	{ | ||||
| 		modeprefix = "spb-"; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		const INT32 skinid = R_SkinAvailableEx(cv_skin[0].string, false); | ||||
| 		if (skinid >= 0 && (skins[skinid]->flags & SF_HIVOLT)) | ||||
| 		{ | ||||
| 			modeprefix = "hivolt-"; | ||||
| 		} | ||||
| 	} | ||||
| 	const char *modeprefix = M_GetRecordMode(); | ||||
| 
 | ||||
| 	if (TA_GuestReplay_Str != NULL) | ||||
| 	{ | ||||
|  | @ -597,20 +586,7 @@ void M_SetGuestReplay(INT32 choice) | |||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	const char *modeprefix = ""; | ||||
| 
 | ||||
| 	if (cv_dummyspbattack.value) | ||||
| 	{ | ||||
| 		modeprefix = "spb-"; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		const INT32 skinid = R_SkinAvailableEx(cv_skin[0].string, false); | ||||
| 		if (skinid >= 0 && (skins[skinid]->flags & SF_HIVOLT)) | ||||
| 		{ | ||||
| 			modeprefix = "hivolt-"; | ||||
| 		} | ||||
| 	} | ||||
| 	const char *modeprefix = M_GetRecordMode(); | ||||
| 
 | ||||
| 	if (FIL_FileExists(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%sguest.lmp", srb2home, timeattackfolder, G_BuildMapName(levellist.choosemap+1), modeprefix))) | ||||
| 	{ | ||||
|  | @ -626,7 +602,7 @@ void M_StartTimeAttack(INT32 choice) | |||
| { | ||||
| 	char *gpath; | ||||
| 	char nameofdemo[256]; | ||||
| 	const char *modeprefix = ""; | ||||
| 	const char *modeprefix = M_GetRecordMode(); | ||||
| 
 | ||||
| 	(void)choice; | ||||
| 
 | ||||
|  | @ -649,16 +625,6 @@ void M_StartTimeAttack(INT32 choice) | |||
| 		{ | ||||
| 			encoremode = true; // guarantees short wipe
 | ||||
| 		} | ||||
| 
 | ||||
| 		modeprefix = "spb-"; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		const INT32 skinid = R_SkinAvailableEx(cv_skin[0].string, false); | ||||
| 		if (skinid >= 0 && (skins[skinid]->flags & SF_HIVOLT)) | ||||
| 		{ | ||||
| 			modeprefix = "hivolt-"; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// DON'T SOFTLOCK
 | ||||
|  |  | |||
|  | @ -287,6 +287,97 @@ static void M_GPBackup(INT32 choice) | |||
| 	M_StartCup(UINT8_MAX); | ||||
| } | ||||
| 
 | ||||
| static boolean M_IsCupQueueable(cupheader_t *cup) | ||||
| { | ||||
| 	levelsearch_t templevelsearch = levellist.levelsearch; // copy levellist so we don't mess with stuff I think
 | ||||
| 	UINT16 ShownCount = 0; | ||||
| 	UINT16 CupCount = 0; | ||||
| 	UINT32 CheckGametype[2] = {TOL_RACE,TOL_BATTLE}; | ||||
| 	 | ||||
| 	templevelsearch.cup = cup; | ||||
| 	 | ||||
| 	UINT8 e, i = 0; | ||||
| 	for (e = 0; e < 2; e++) | ||||
| 	{ | ||||
| 		templevelsearch.typeoflevel = CheckGametype[e]; | ||||
| 		ShownCount += M_CountLevelsToShowInList(&templevelsearch); | ||||
| 	} | ||||
| 	//CONS_Printf(M_GetText("ShownCount: %d\n"), ShownCount);
 | ||||
| 	UINT16 checkmap = NEXTMAP_INVALID; | ||||
| 	for (i = 0; i < CUPCACHE_SPECIAL; i++) | ||||
| 	{ | ||||
| 		checkmap = templevelsearch.cup->cachedlevels[i]; | ||||
| 		if (checkmap == NEXTMAP_INVALID) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
| 		CupCount++; | ||||
| 	} | ||||
| 	//CONS_Printf(M_GetText("CupCount: %d\n"), CupCount);
 | ||||
| 	if (ShownCount >= CupCount) // greater than is used to ensure multi-gametype maps don't accidentally cause this to return false.
 | ||||
| 		return true; | ||||
| 	 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static void M_CupStartResponse(INT32 ch) | ||||
| { | ||||
| 	if (ch != MA_YES) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!(server || (IsPlayerAdmin(consoleplayer)))) | ||||
| 		return; | ||||
| 	 | ||||
| 	M_LevelConfirmHandler(); | ||||
| } | ||||
| 
 | ||||
| static void M_CupQueueResponse(INT32 ch) | ||||
| { | ||||
| 	if (ch != MA_YES) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!(server || (IsPlayerAdmin(consoleplayer)))) | ||||
| 		return; | ||||
| 	 | ||||
| 	cupheader_t *queuedcup = cupgrid.builtgrid[CUPMENU_CURSORID]; | ||||
| 	 | ||||
| 	M_CupQueueHandler(queuedcup); | ||||
| 	 | ||||
| 	S_StartSound(NULL, sfx_gshe2); | ||||
| 	 | ||||
| 	while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX) | ||||
| 		menuqueue.size--; | ||||
| 	 | ||||
| 	if (!netgame) | ||||
| 	{ | ||||
| 		M_StartMessage("Cup Queue", | ||||
| 			va(M_GetText( | ||||
| 			"You just queued %s CUP.\n" | ||||
| 			"\n" | ||||
| 			"Do you want to start the\n" | ||||
| 			"cup immediately?\n" | ||||
| 			), queuedcup->realname | ||||
| 			), &M_CupStartResponse, MM_YESNO, | ||||
| 			"Here we go!", | ||||
| 			"On second thought..." | ||||
| 		); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		M_StartMessage("Cup Queue", | ||||
| 			va(M_GetText( | ||||
| 			"You just queued %s CUP.\n" | ||||
| 			"\n" | ||||
| 			"Do you want to queue it\n" | ||||
| 			"for everyone?\n" | ||||
| 			), queuedcup->realname | ||||
| 			), &M_CupStartResponse, MM_YESNO, | ||||
| 			"Queue em up!", | ||||
| 			"Not yet" | ||||
| 		); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void M_CupSelectHandler(INT32 choice) | ||||
| { | ||||
| 	const UINT8 pid = 0; | ||||
|  | @ -429,6 +520,63 @@ void M_CupSelectHandler(INT32 choice) | |||
| 			S_StartSound(NULL, sfx_s3k63); | ||||
| 		} | ||||
| 	} | ||||
| 	// Queue a cup for match race and netgames. See levelselect.c for most of how this actually works.
 | ||||
| 	else if (levellist.canqueue && M_MenuButtonPressed(pid, MBT_Z)) | ||||
| 	{ | ||||
| 		M_SetMenuDelay(pid); | ||||
| 		 | ||||
| 		if ((cupgrid.builtgrid[CUPMENU_CURSORID] == &dummy_lostandfound) || (cupgrid.builtgrid[CUPMENU_CURSORID] == NULL)) | ||||
| 			S_StartSound(NULL, sfx_gshe7); | ||||
| 			 | ||||
| 		else if (!M_IsCupQueueable(cupgrid.builtgrid[CUPMENU_CURSORID])) | ||||
| 		{ | ||||
| 			S_StartSound(NULL, sfx_s3kb2); | ||||
| 			M_StartMessage("Back to the Grand Prix!", "Can't queue a cup you haven't fully unlocked!", NULL, MM_NOTHING, NULL, NULL); | ||||
| 		} | ||||
| 		 | ||||
| 		// Better to avoid any headaches here - pass the buck to the Extra button.
 | ||||
| 		else if (roundqueue.size) | ||||
| 		{ | ||||
| 			S_StartSound(NULL, sfx_s3kb2); | ||||
| 			M_StartMessage("Queue is not empty!", "Clear the queue before trying to queue a cup!", NULL, MM_NOTHING, NULL, NULL); | ||||
| 			return; | ||||
| 		} | ||||
| 			 | ||||
| 		else | ||||
| 		{ | ||||
| 			// We're not queueing Battle maps if we're in single-player Match Race.
 | ||||
| 			if (!levellist.netgame && (cv_splitplayers.value == 1) && !netgame) | ||||
| 			{ | ||||
| 				M_StartMessage("Cup Queue", | ||||
| 					va(M_GetText( | ||||
| 					"This will queue all Race courses in this cup.\n" | ||||
| 					"\n" | ||||
| 					"Any rounds already in the queue will be cleared out.\n" | ||||
| 					"\n" | ||||
| 					"Do you want to queue the cup?\n" | ||||
| 					)), &M_CupQueueResponse, MM_YESNO, | ||||
| 					"Let's do it!", | ||||
| 					"Nah."); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				M_StartMessage("Cup Queue", | ||||
| 					va(M_GetText( | ||||
| 					"This will queue the entire cup, including both Race and Battle courses.\n" | ||||
| 					"\n" | ||||
| 					"Any rounds already in the queue will be cleared out.\n" | ||||
| 					"\n" | ||||
| 					"Do you want to queue the cup?\n" | ||||
| 					)), &M_CupQueueResponse, MM_YESNO, | ||||
| 					"Let's do it!", | ||||
| 					"Nah."); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else if (levellist.canqueue && M_MenuExtraPressed(pid)) | ||||
| 	{ | ||||
| 		M_ClearQueueHandler(); | ||||
| 	} | ||||
| 	else if (M_MenuBackPressed(pid)) | ||||
| 	{ | ||||
| 		M_SetMenuDelay(pid); | ||||
|  | @ -443,4 +591,6 @@ void M_CupSelectHandler(INT32 choice) | |||
| void M_CupSelectTick(void) | ||||
| { | ||||
| 	cupgrid.previewanim++; | ||||
| 	// Shoving this here for cup queue purposes.
 | ||||
| 	M_LevelSelectTick(); | ||||
| } | ||||
|  |  | |||
|  | @ -832,8 +832,10 @@ void M_LevelSelected(INT16 add, boolean menuupdate) | |||
| static void M_MenuQueueStopSend(INT32 ch) | ||||
| { | ||||
| 	(void)ch; | ||||
| 
 | ||||
| 	 | ||||
| 	memset(&menuqueue, 0, sizeof(struct menuqueue)); | ||||
| 	 | ||||
| 	menuqueue.clearing = false; | ||||
| } | ||||
| 
 | ||||
| static void M_MenuQueueSelectedLocal(void) | ||||
|  | @ -905,6 +907,92 @@ static void M_MenuQueueSelectedLocal(void) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Copy-pasted and edited from G_GPCupIntoRoundQueue
 | ||||
| void M_CupQueueHandler(cupheader_t *cup) | ||||
| { | ||||
| 	UINT8 i, levelindex = 0, bonusindex = 0; | ||||
| 	UINT8 bonusmodulo = max(1, (cup->numlevels+1)/(cup->numbonus+1)); | ||||
| 	UINT16 cupLevelNum; | ||||
| 	INT32 gtcheck; | ||||
| 	 | ||||
| 	// We shouldn't get to this point while there's rounds queued, but if we do, get outta there.
 | ||||
| 	if (roundqueue.size) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	menuqueue.size = 0; | ||||
| 
 | ||||
| 	// Levels are added to the queue in the following pattern.
 | ||||
| 	// For 5 Race rounds and 2 Bonus rounds, the most common case:
 | ||||
| 	//    race - race - BONUS - race - race - BONUS - race
 | ||||
| 	// The system is flexible enough to permit other arrangements.
 | ||||
| 	// However, we just want to keep the pacing even & consistent.
 | ||||
| 	while (levelindex < cup->numlevels) | ||||
| 	{ | ||||
| 		memset(menuqueue.entries+menuqueue.size, 0, sizeof(roundentry_t)); | ||||
| 		 | ||||
| 		// Fill like two or three Race maps.
 | ||||
| 		for (i = 0; i < bonusmodulo; i++) | ||||
| 		{ | ||||
| 			cupLevelNum = cup->cachedlevels[levelindex]; | ||||
| 
 | ||||
| 			if (cupLevelNum >= nummapheaders) | ||||
| 			{ | ||||
| 				// Just skip the map if it's invalid.
 | ||||
| 				continue; | ||||
| 			} | ||||
| 			 | ||||
| 			if ((mapheaderinfo[cupLevelNum]->typeoflevel & TOL_RACE) == TOL_RACE) | ||||
| 			{ | ||||
| 				gtcheck = GT_RACE; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				gtcheck = mapheaderinfo[cupLevelNum]->typeoflevel; | ||||
| 			} | ||||
| 
 | ||||
| 			menuqueue.entries[menuqueue.size].mapnum = cupLevelNum; | ||||
| 			menuqueue.entries[menuqueue.size].gametype = gtcheck; | ||||
| 			menuqueue.entries[menuqueue.size].encore = (cv_kartencore.value == 1); | ||||
| 			 | ||||
| 			menuqueue.size++; | ||||
| 
 | ||||
| 			levelindex++; | ||||
| 			if (levelindex >= cup->numlevels) | ||||
| 				break; | ||||
| 		} | ||||
| 
 | ||||
| 		// Attempt to add an interstitial Battle round.
 | ||||
| 		// If we're in singleplayer Match Race, just skip this.
 | ||||
| 		if ((levelindex < cup->numlevels | ||||
| 			&& bonusindex < cup->numbonus) && (levellist.netgame || (cv_splitplayers.value > 1) || netgame)) | ||||
| 		{ | ||||
| 			cupLevelNum = cup->cachedlevels[CUPCACHE_BONUS + bonusindex]; | ||||
| 
 | ||||
| 			if (cupLevelNum < nummapheaders) | ||||
| 			{ | ||||
| 				if ((mapheaderinfo[cupLevelNum]->typeoflevel & TOL_BATTLE) == TOL_BATTLE) | ||||
| 				{ | ||||
| 					gtcheck = GT_BATTLE; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					gtcheck = mapheaderinfo[cupLevelNum]->typeoflevel; | ||||
| 				} | ||||
| 				// In the case of Bonus rounds, we simply skip invalid maps.
 | ||||
| 				menuqueue.entries[menuqueue.size].mapnum = cupLevelNum; | ||||
| 				menuqueue.entries[menuqueue.size].gametype = gtcheck; | ||||
| 				menuqueue.entries[menuqueue.size].encore = (cv_kartencore.value == 1); | ||||
| 				 | ||||
| 				menuqueue.size++; | ||||
| 			} | ||||
| 
 | ||||
| 			bonusindex++; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| boolean M_LevelSelectCupSwitch(boolean next, boolean skipones) | ||||
| { | ||||
| 	levelsearch_t templevelsearch = levellist.levelsearch; | ||||
|  | @ -1002,6 +1090,40 @@ static void M_MenuQueueResponse(INT32 ch) | |||
| 	SendNetXCmd(XD_EXITLEVEL, NULL, 0); | ||||
| } | ||||
| 
 | ||||
| // Ripped out of LevelSelectHandler for use in cup queueing from cupselect.c
 | ||||
| void M_LevelConfirmHandler(void) | ||||
| { | ||||
| 	// Starting immediately OR importing queue
 | ||||
| 	 | ||||
| 	while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX) | ||||
| 			menuqueue.size--; | ||||
| 
 | ||||
| 	if (!levellist.canqueue || !menuqueue.size) | ||||
| 	{ | ||||
| 		M_LevelSelected(levellist.cursor, true); | ||||
| 	} | ||||
| 	else if (netgame) | ||||
| 	{ | ||||
| 		menuqueue.anchor = roundqueue.size; | ||||
| 		menuqueue.sending = 1; | ||||
| 
 | ||||
| 		M_StartMessage("Queueing Rounds", | ||||
| 			va(M_GetText( | ||||
| 			"Attempting to send %d Round%s...\n" | ||||
| 			"\n" | ||||
| 			"If this is taking longer than you\n" | ||||
| 			"expect, exit out of this message.\n" | ||||
| 			), menuqueue.size, (menuqueue.size == 1 ? "" : "s") | ||||
| 			), &M_MenuQueueStopSend, MM_NOTHING, | ||||
| 			NULL, | ||||
| 			"This is taking too long..." | ||||
| 		); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		M_MenuQueueSelectedLocal(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void M_ClearQueueResponse(INT32 ch) | ||||
| { | ||||
|  | @ -1012,18 +1134,56 @@ static void M_ClearQueueResponse(INT32 ch) | |||
| 		return; | ||||
| 
 | ||||
| 	S_StartSound(NULL, sfx_slip); | ||||
| 
 | ||||
| 	if (netgame) | ||||
| 	 | ||||
| 	if (!netgame) | ||||
| 		memset(&roundqueue, 0, sizeof(struct roundqueue)); | ||||
| 	if (netgame && (roundqueue.size != 0)) | ||||
| 	{ | ||||
| 		if (roundqueue.size) | ||||
| 		{ | ||||
| 			Handle_MapQueueSend(0, ROUNDQUEUE_CMD_CLEAR, false); | ||||
| 		} | ||||
| 		return; | ||||
| 		menuqueue.clearing = true; | ||||
| 		Handle_MapQueueSend(0, ROUNDQUEUE_CMD_CLEAR, false); | ||||
| 		M_StartMessage("Clearing Rounds", | ||||
| 			va(M_GetText( | ||||
| 			"Attempting to clear %d Round%s...\n" | ||||
| 			"\n" | ||||
| 			"If this is taking longer than you\n" | ||||
| 			"expect, exit out of this message.\n" | ||||
| 			), roundqueue.size, (roundqueue.size == 1 ? "" : "s") | ||||
| 			), &M_MenuQueueStopSend, MM_NOTHING, | ||||
| 			NULL, | ||||
| 			"This is taking too long..." | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	memset(&roundqueue, 0, sizeof(struct roundqueue)); | ||||
| } | ||||
| 
 | ||||
| // Ripped out of LevelSelectHandler for use in queue clearing from cupselect.c
 | ||||
| void M_ClearQueueHandler(void) | ||||
| { | ||||
| 	while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX) | ||||
| 		menuqueue.size--; | ||||
| 
 | ||||
| 	if (menuqueue.size) | ||||
| 	{ | ||||
| 		S_StartSound(NULL, sfx_shldls); | ||||
| 		menuqueue.size--; | ||||
| 	} | ||||
| 	else if (roundqueue.size) | ||||
| 	{ | ||||
| 		M_StartMessage("Queue Clearing", | ||||
| 			va(M_GetText( | ||||
| 			"There %s %d Round%s of play queued.\n" | ||||
| 			"\n" | ||||
| 			"Do you want to empty the queue?\n" | ||||
| 			), | ||||
| 			(roundqueue.size == 1 ? "is" : "are"), | ||||
| 			roundqueue.size, | ||||
| 			(roundqueue.size == 1 ? "" : "s") | ||||
| 			), &M_ClearQueueResponse, MM_YESNO, | ||||
| 			"Time to start fresh", | ||||
| 			"Not right now" | ||||
| 		); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void M_LevelSelectHandler(INT32 choice) | ||||
| { | ||||
| 	const UINT8 pid = 0; | ||||
|  | @ -1074,38 +1234,9 @@ void M_LevelSelectHandler(INT32 choice) | |||
| 
 | ||||
| 	if (M_MenuConfirmPressed(pid)) | ||||
| 	{ | ||||
| 		// Starting immediately OR importing queue
 | ||||
| 
 | ||||
| 		M_SetMenuDelay(pid); | ||||
| 
 | ||||
| 		while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX) | ||||
| 			menuqueue.size--; | ||||
| 
 | ||||
| 		if (!levellist.canqueue || !menuqueue.size) | ||||
| 		{ | ||||
| 			M_LevelSelected(levellist.cursor, true); | ||||
| 		} | ||||
| 		else if (netgame) | ||||
| 		{ | ||||
| 			menuqueue.anchor = roundqueue.size; | ||||
| 			menuqueue.sending = 1; | ||||
| 
 | ||||
| 			M_StartMessage("Queueing Rounds", | ||||
| 				va(M_GetText( | ||||
| 				"Attempting to send %d Round%s...\n" | ||||
| 				"\n" | ||||
| 				"If this is taking longer than you\n" | ||||
| 				"expect, exit out of this message.\n" | ||||
| 				), menuqueue.size, (menuqueue.size == 1 ? "" : "s") | ||||
| 				), &M_MenuQueueStopSend, MM_NOTHING, | ||||
| 				NULL, | ||||
| 				"This is taking too long..." | ||||
| 			); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			M_MenuQueueSelectedLocal(); | ||||
| 		} | ||||
| 		M_LevelConfirmHandler(); | ||||
| 	} | ||||
| 	else if (levellist.canqueue && M_MenuButtonPressed(pid, MBT_Z)) | ||||
| 	{ | ||||
|  | @ -1138,30 +1269,7 @@ void M_LevelSelectHandler(INT32 choice) | |||
| 	} | ||||
| 	else if (levellist.canqueue && M_MenuExtraPressed(pid)) | ||||
| 	{ | ||||
| 		while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX) | ||||
| 			menuqueue.size--; | ||||
| 
 | ||||
| 		if (menuqueue.size) | ||||
| 		{ | ||||
| 			S_StartSound(NULL, sfx_shldls); | ||||
| 			menuqueue.size--; | ||||
| 		} | ||||
| 		else if (roundqueue.size) | ||||
| 		{ | ||||
| 			M_StartMessage("Queue Clearing", | ||||
| 				va(M_GetText( | ||||
| 				"There %s %d Round%s of play queued.\n" | ||||
| 				"\n" | ||||
| 				"Do you want to empty the queue?\n" | ||||
| 				), | ||||
| 				(roundqueue.size == 1 ? "is" : "are"), | ||||
| 				roundqueue.size, | ||||
| 				(roundqueue.size == 1 ? "" : "s") | ||||
| 				), &M_ClearQueueResponse, MM_YESNO, | ||||
| 				"Time to start fresh", | ||||
| 				"Not right now" | ||||
| 			); | ||||
| 		} | ||||
| 		M_ClearQueueHandler(); | ||||
| 	} | ||||
| 	else if (M_MenuBackPressed(pid)) | ||||
| 	{ | ||||
|  | @ -1176,9 +1284,20 @@ void M_LevelSelectHandler(INT32 choice) | |||
| 
 | ||||
| void M_LevelSelectTick(void) | ||||
| { | ||||
| 	if (menuqueue.clearing) | ||||
| 	{ | ||||
| 		if (roundqueue.size != 0) | ||||
| 			return; | ||||
| 		menuqueue.clearing = false; | ||||
| 		if (!menuqueue.cupqueue) | ||||
| 			M_StopMessage(MA_NONE); | ||||
| 		else | ||||
| 			menuqueue.cupqueue = false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!menuqueue.sending) | ||||
| 		return; | ||||
| 
 | ||||
| 	 | ||||
| 	if ((menuqueue.sending <= menuqueue.size) // Sending
 | ||||
| 		&& (roundqueue.size >= menuqueue.anchor)) // Didn't get it wiped
 | ||||
| 	{ | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| #include "../../d_netcmd.h" | ||||
| #include "../../i_time.h" | ||||
| #include "../../k_menu.h" | ||||
| #include "../../hu_stuff.h" | ||||
| #include "../../k_grandprix.h" // K_CanChangeRules | ||||
| #include "../../m_cond.h" | ||||
| #include "../../s_sound.h" | ||||
|  | @ -125,6 +126,9 @@ void M_OpenPauseMenu(void) | |||
| 	pausemenu.openoffset.dist = 0; | ||||
| 	pausemenu.closing = false; | ||||
| 
 | ||||
| 	// Fix specific input error regarding closing netgame chat with escape while a controller is connected (only on Windows?)
 | ||||
| 	chat_keydown = false; | ||||
| 
 | ||||
| 	itemOn = currentMenu->lastOn = mpause_continue;	// Make sure we select "RESUME GAME" by default
 | ||||
| 
 | ||||
| 	// Now the hilarious balancing act of deciding what options should be enabled and which ones shouldn't be!
 | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ menuitem_t PAUSE_PlaybackMenu[] = | |||
| 	{IT_CALL   | IT_STRING, "Hide Menu",			NULL, "M_PHIDE",	{.routine = M_SelectableClearMenus},	  0, 0}, | ||||
| 
 | ||||
| 	{IT_CALL   | IT_STRING, "Restart",				NULL, "M_PRSTRT",	{.routine = M_PlaybackRewind},			 20, 0}, | ||||
| 	{IT_CALL   | IT_STRING, "Rewind 5 seconds",		NULL, "M_PREW",		{.routine = M_PlaybackRewind},			 36, 0}, | ||||
| 	{IT_CALL   | IT_STRING, "Rewind 10 seconds",	NULL, "M_PREW",		{.routine = M_PlaybackRewind},			 36, 0}, | ||||
| 	{IT_CALL   | IT_STRING, "Pause",				NULL, "M_PPAUSE",	{.routine = M_PlaybackPause},			 52, 0}, | ||||
| 	{IT_CALL   | IT_STRING, "Fast-Forward",			NULL, "M_PFFWD",	{.routine = M_PlaybackFastForward},		 68, 0}, | ||||
| 	{IT_CALL   | IT_STRING, "Resume",				NULL, "M_PRESUM",	{.routine = M_PlaybackPause},			 52, 0}, | ||||
|  | @ -220,9 +220,9 @@ void M_PlaybackRewind(INT32 choice) | |||
| 
 | ||||
| 	if (demo.simplerewind) | ||||
| 	{ | ||||
| 		if (curleveltime > 5*TICRATE) | ||||
| 		if (curleveltime > 10*TICRATE) | ||||
| 		{ | ||||
| 			g_fast_forward = curleveltime - (5 * TICRATE); | ||||
| 			g_fast_forward = curleveltime - (10 * TICRATE); | ||||
| 			g_fast_forward_clock_stop = INFTICS; //I_GetTime() + 2 * TICRATE; -- maybe?
 | ||||
| 		} | ||||
| 		else | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ TuneManager g_tunes; | |||
| 
 | ||||
| void Music_Init(void) | ||||
| { | ||||
| 	// Many tunes below now have their default songs set in Music_TuneReset. Check there first for changing those.
 | ||||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("level"); | ||||
| 
 | ||||
|  | @ -55,21 +56,21 @@ void Music_Init(void) | |||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("battle_overtime", g_tunes.find("level")); | ||||
| 
 | ||||
| 		tune.song = "shwdwn"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 11; | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("battle_overtime_stress", g_tunes.find("battle_overtime")); | ||||
| 
 | ||||
| 		tune.song = "shwdn2"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 10; | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("grow"); | ||||
| 
 | ||||
| 		tune.song = "kgrow"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 20; | ||||
| 		tune.resume_fade_in = 200; | ||||
| 		tune.use_level_volume = true; | ||||
|  | @ -78,7 +79,7 @@ void Music_Init(void) | |||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("invinc"); | ||||
| 
 | ||||
| 		tune.song = "kinvnc"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 21; | ||||
| 		tune.use_level_volume = true; | ||||
| 	} | ||||
|  | @ -86,7 +87,7 @@ void Music_Init(void) | |||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("finish_silence"); | ||||
| 
 | ||||
| 		tune.song = ""; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 30; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -100,7 +101,7 @@ void Music_Init(void) | |||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("comeon"); | ||||
| 
 | ||||
| 		tune.song = "chalng"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 35; | ||||
| 		tune.loop = false; | ||||
| 	} | ||||
|  | @ -116,7 +117,7 @@ void Music_Init(void) | |||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("vote"); | ||||
| 
 | ||||
| 		tune.song = "vote"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 50; | ||||
| 		tune.credit = true; | ||||
| 	} | ||||
|  | @ -124,14 +125,14 @@ void Music_Init(void) | |||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("vote_suspense"); | ||||
| 
 | ||||
| 		tune.song = "voteea"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 51; | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("vote_end"); | ||||
| 
 | ||||
| 		tune.song = "voteeb"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 52; | ||||
| 		tune.loop = false; | ||||
| 	} | ||||
|  | @ -139,14 +140,14 @@ void Music_Init(void) | |||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("wait"); | ||||
| 
 | ||||
| 		tune.song = "WAIT2J"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 60; | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("title"); | ||||
| 
 | ||||
| 		tune.song = "_title"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 100; | ||||
| 		tune.resist = true; | ||||
| 	} | ||||
|  | @ -167,7 +168,7 @@ void Music_Init(void) | |||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("credits_silence"); | ||||
| 
 | ||||
| 		tune.song = ""; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 100; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -175,7 +176,7 @@ void Music_Init(void) | |||
| 		Tune& tune = g_tunes.insert("credits"); | ||||
| 
 | ||||
| 		tune.priority = 101; | ||||
| 		tune.song = "_creds"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.credit = true; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -213,10 +214,12 @@ void Music_Init(void) | |||
| 	{ | ||||
| 		Tune& tune = g_tunes.insert("lawyer"); | ||||
| 
 | ||||
| 		tune.song = "lawyer"; | ||||
| 		tune.song = ""; // Music_TuneReset
 | ||||
| 		tune.priority = 35; | ||||
| 		tune.loop = false; | ||||
| 	} | ||||
| 	 | ||||
| 	Music_TuneReset(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -522,3 +525,21 @@ void Music_ResetLevelVolume(void) | |||
| { | ||||
| 	g_tunes.level_volume(100, true); | ||||
| } | ||||
| 
 | ||||
| void Music_TuneReset(void) | ||||
| { | ||||
| 	Music_Remap("battle_overtime", "shwdwn"); | ||||
| 	Music_Remap("battle_overtime_stress", "shwdn2"); | ||||
| 	Music_Remap("grow", "kgrow"); | ||||
| 	Music_Remap("invinc", "kinvnc"); | ||||
| 	Music_Remap("finish_silence", ""); | ||||
| 	Music_Remap("comeon", "chalng"); | ||||
| 	Music_Remap("vote", "vote"); | ||||
| 	Music_Remap("vote_suspense", "voteea"); | ||||
| 	Music_Remap("vote_end", "voteeb"); | ||||
| 	Music_Remap("wait", "WAIT2J"); | ||||
| 	Music_Remap("title", "_title"); | ||||
| 	Music_Remap("credits_silence", ""); | ||||
| 	Music_Remap("credits", "_creds"); | ||||
| 	Music_Remap("lawyer", "lawyer"); | ||||
| } | ||||
|  |  | |||
|  | @ -207,6 +207,10 @@ void Music_Tick(void); | |||
| // the music plays again when re-enabled.
 | ||||
| void Music_Flip(void); | ||||
| 
 | ||||
| // Resets all non-dynamic tunes to default values.
 | ||||
| // Keeps ACS music remapping from playing havoc after a map.
 | ||||
| void Music_TuneReset(void); | ||||
| 
 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } // extern "C"
 | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ | |||
| #include "../r_main.h" | ||||
| #include "../tables.h" | ||||
| #include "../s_sound.h" | ||||
| #include "../k_kart.h" | ||||
| 
 | ||||
| /* An object may not be visible on the same tic:
 | ||||
|    1) that it spawned | ||||
|  | @ -85,7 +86,7 @@ bool award_target(mobj_t* mobj) | |||
| 		if (rebound_timer(mobj) < 1) | ||||
| 		{ | ||||
| 			player->itemtype = KITEM_GACHABOM; | ||||
| 			player->itemamount++; | ||||
| 			K_AdjustPlayerItemAmount(player, 1); | ||||
| 			if (player->roundconditions.gachabom_miser == 1) | ||||
| 				player->roundconditions.gachabom_miser = 0; | ||||
| 
 | ||||
|  |  | |||
|  | @ -612,7 +612,7 @@ Obj_GardenTopThrow (player_t *player) | |||
| 	} | ||||
| 
 | ||||
| 	if (player->itemamount > 0) | ||||
| 		player->itemamount--; | ||||
| 		K_AdjustPlayerItemAmount(player, -1); | ||||
| 
 | ||||
| 	if (player->itemamount <= 0) | ||||
| 		player->itemtype = KITEM_NONE; | ||||
|  |  | |||
|  | @ -325,7 +325,7 @@ move_to_player (mobj_t *hyu) | |||
| 
 | ||||
| 	// For first place only: cap hyudoro speed at 50%
 | ||||
| 	// target player's kart speed
 | ||||
| 	if (target->player && target->player->position == 1) | ||||
| 	if (target->player && target->player->leaderpenalty) | ||||
| 	{ | ||||
| 		const fixed_t normalspeed = | ||||
| 			K_GetKartSpeed(target->player, false, false) / 2; | ||||
|  | @ -582,7 +582,7 @@ hyudoro_patrol_hit_player | |||
| 	S_StartSound(toucher, sfx_s3k92); | ||||
| 
 | ||||
| 	/* do not make 1st place invisible */ | ||||
| 	if (player->position != 1) | ||||
| 	if (player->leaderpenalty == 0) | ||||
| 	{ | ||||
| 		player->hyudorotimer = hyudorotime; | ||||
| 	} | ||||
|  | @ -625,7 +625,7 @@ award_immediately (mobj_t *hyu) | |||
| 
 | ||||
| 	if (player) | ||||
| 	{ | ||||
| 		if (player->position == 1) | ||||
| 		if (player->leaderpenalty) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
|  | @ -742,7 +742,7 @@ blend_hover_hyudoro (mobj_t *hyu) | |||
| 
 | ||||
| 	/* 1st place: Hyudoro stack is unusable, so make a visual
 | ||||
| 	   indication */ | ||||
| 	if (player->position == 1) | ||||
| 	if (player->leaderpenalty) | ||||
| 	{ | ||||
| 		hyu->renderflags |= RF_MODULATE; | ||||
| 		trail_glow(hyu); | ||||
|  |  | |||
|  | @ -325,10 +325,9 @@ void Obj_OrbinautThrown(mobj_t *th, fixed_t finalSpeed, fixed_t dir) | |||
| 	{ | ||||
| 		th->color = orbinaut_owner(th)->player->skincolor; | ||||
| 
 | ||||
| 		const mobj_t *owner = orbinaut_owner(th); | ||||
| 		const ffloor_t *rover = P_IsObjectFlipped(owner) ? owner->ceilingrover : owner->floorrover; | ||||
| 		const boolean ownerwaterrun = K_WaterRun(orbinaut_owner(th)); | ||||
| 
 | ||||
| 		if (dir >= 0 && rover && (rover->fofflags & FOF_SWIMMABLE)) | ||||
| 		if (dir >= 0 && ownerwaterrun) | ||||
| 		{ | ||||
| 			// The owner can run on water, so we should too!
 | ||||
| 			orbinaut_flags(th) |= ORBI_WATERSKI; | ||||
|  |  | |||
|  | @ -88,7 +88,7 @@ struct Shoe : Mobj | |||
| 	bool valid() const { return Mobj::valid(follow()) && follow()->valid() && Mobj::valid(chain()); } | ||||
| 
 | ||||
| 	Fixed minDist() const { return 200 * mapobjectscale; } | ||||
| 	Fixed maxDist() const { return 800 * mapobjectscale; } | ||||
| 	Fixed maxDist() const { return 500 * mapobjectscale; } | ||||
| 
 | ||||
| 	angle_t followAngle() const { return R_PointToAngle2(x, y, follow()->x, follow()->y); } | ||||
| 	Fixed followDistance() const { return FixedHypot(x - follow()->x, y - follow()->y); } | ||||
|  | @ -237,7 +237,7 @@ private: | |||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			thrust(a, 8 * mapobjectscale); | ||||
| 			thrust(a, 10 * mapobjectscale); | ||||
| 
 | ||||
| 			Fixed maxSpeed = 32 * mapobjectscale; | ||||
| 			Fixed speed = FixedHypot(momx, momy); | ||||
|  |  | |||
|  | @ -463,9 +463,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) | |||
| 
 | ||||
| 					player->itemtype = special->threshold; | ||||
| 					if ((UINT16)(player->itemamount) + special->movecount > 255) | ||||
| 						player->itemamount = 255; | ||||
| 						K_SetPlayerItemAmount(player, 255); | ||||
| 					else | ||||
| 						player->itemamount += special->movecount; | ||||
| 						K_AdjustPlayerItemAmount(player, special->movecount); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
|  | @ -1800,16 +1800,17 @@ void P_UpdateRemovedOrbital(mobj_t *target, mobj_t *inflictor, mobj_t *source) | |||
| 				{ | ||||
| 					if (target->target->hnext && !P_MobjWasRemoved(target->target->hnext)) | ||||
| 						K_KillBananaChain(target->target->hnext, inflictor, source); | ||||
| 					target->target->player->itemamount = 0; | ||||
| 
 | ||||
| 					K_SetPlayerItemAmount(target->target->player, 0); | ||||
| 				} | ||||
| 				else if (target->target->player->itemamount) | ||||
| 					target->target->player->itemamount--; | ||||
| 					K_AdjustPlayerItemAmount(target->target->player, -1); | ||||
| 			} | ||||
| 			else if ((target->type == MT_ORBINAUT_SHIELD && target->target->player->itemtype == KITEM_ORBINAUT) // orbit items
 | ||||
| 				|| (target->type == MT_JAWZ_SHIELD && target->target->player->itemtype == KITEM_JAWZ)) | ||||
| 			{ | ||||
| 				if (target->target->player->itemamount) | ||||
| 					target->target->player->itemamount--; | ||||
| 					K_AdjustPlayerItemAmount(target->target->player, -1); | ||||
| 				if (target->lastlook != 0) | ||||
| 				{ | ||||
| 					K_RepairOrbitChain(target); | ||||
|  | @ -2206,15 +2207,15 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget | |||
| 				if (target->threshold < 1 || target->threshold >= NUMKARTITEMS) // bruh moment prevention
 | ||||
| 				{ | ||||
| 					player->itemtype = KITEM_SAD; | ||||
| 					player->itemamount = 1; | ||||
| 					K_SetPlayerItemAmount(player, 1); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					player->itemtype = target->threshold; | ||||
| 					if (K_GetShieldFromItem(player->itemtype) != KSHIELD_NONE) // never give more than 1 shield
 | ||||
| 						player->itemamount = 1; | ||||
| 						K_SetPlayerItemAmount(player, 1); | ||||
| 					else | ||||
| 						player->itemamount = max(1, target->movecount); | ||||
| 						K_SetPlayerItemAmount(player, max(1, target->movecount)); | ||||
| 				} | ||||
| 				player->karthud[khud_itemblink] = TICRATE; | ||||
| 				player->karthud[khud_itemblinkmode] = 0; | ||||
|  | @ -3953,7 +3954,9 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da | |||
| 					sfx = sfx_s3k45; | ||||
| 				} | ||||
| 				else if (player->hyudorotimer > 0) | ||||
| 					; | ||||
| 				{ | ||||
| 					clash = false; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					invincible = false; | ||||
|  |  | |||
							
								
								
									
										10
									
								
								src/p_map.c
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								src/p_map.c
									
										
									
									
									
								
							|  | @ -1378,12 +1378,12 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) | |||
| 		if (g_tm.thing->z + g_tm.thing->height < thing->z) | ||||
| 			return BMIT_CONTINUE; // underneath
 | ||||
| 
 | ||||
| 		if (g_tm.thing->player && g_tm.thing->player && g_tm.thing->player->tumbleBounces > 0) | ||||
| 		if (g_tm.thing->player && g_tm.thing->player->tumbleBounces) | ||||
| 		{ | ||||
| 			return BMIT_CONTINUE; | ||||
| 			if (thing->type == MT_SPIKE || G_CompatLevel(0x0011)) | ||||
| 				return BMIT_CONTINUE; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!P_IsObjectOnGround(g_tm.thing) && g_tm.thing->momz * P_MobjFlip(g_tm.thing) < 0) // fell into it
 | ||||
| 		else if (!P_IsObjectOnGround(g_tm.thing) && g_tm.thing->momz * P_MobjFlip(g_tm.thing) < 0) // fell into it
 | ||||
| 		{ | ||||
| 			P_DamageMobj(g_tm.thing, thing, thing, 1, DMG_TUMBLE); | ||||
| 
 | ||||
|  | @ -1398,6 +1398,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) | |||
| 		{ | ||||
| 			if ( | ||||
| 				thing->type == MT_WALLSPIKE | ||||
| 				&& G_CompatLevel(0x0011) | ||||
| 				&& g_tm.thing->health | ||||
| 				&& g_tm.thing->player | ||||
| 				&& (g_tm.thing->player->justbumped < bumptime-2) | ||||
|  | @ -1856,6 +1857,7 @@ boolean P_IsLineTripWire(const line_t *ld) | |||
| 	return ld->tripwire; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static boolean P_UsingStepUp(mobj_t *thing) | ||||
| { | ||||
| 	if (thing->flags & MF_NOCLIP) | ||||
|  |  | |||
							
								
								
									
										77
									
								
								src/p_mobj.c
									
										
									
									
									
								
							
							
						
						
									
										77
									
								
								src/p_mobj.c
									
										
									
									
									
								
							|  | @ -8120,9 +8120,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj) | |||
| 		mobj->renderflags |= (RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(player)); | ||||
| 
 | ||||
| 		// Alright, let's just handle all the sfx down here
 | ||||
| 		boolean not_invinc_or_grow = player->invincibilitytimer == 0 && player->growshrinktimer <= 0; | ||||
| 		boolean not_perma_tripwireok = player->invincibilitytimer == 0 && player->growshrinktimer <= 0 && player->hyudorotimer == 0; | ||||
| 
 | ||||
| 		if (P_IsDisplayPlayer(player) && not_invinc_or_grow && not_respawning) | ||||
| 		if (P_IsDisplayPlayer(player) && not_perma_tripwireok && not_respawning) | ||||
| 		{ | ||||
| 			UINT8 MIN_VOLUME = 25; | ||||
| 			UINT8 MAX_VOLUME = 75; | ||||
|  | @ -8202,13 +8202,34 @@ static boolean P_MobjRegularThink(mobj_t *mobj) | |||
| 
 | ||||
| 			trans = NUMTRANSMAPS - trans; | ||||
| 
 | ||||
| 			if ((trans >= NUMTRANSMAPS) // not a valid visibility
 | ||||
| 				|| (myspeed < (tripspeed - basespeed/2) && (leveltime & 1)) // < 150% flickering
 | ||||
| 				|| (mobj->target->player->tripwirePass < TRIPWIRE_BOOST) // Not strong enough to make an aura
 | ||||
| 				|| mobj->target->player->flamedash) // Flameshield dash
 | ||||
| 			int invinc_rotation_delay = 2; | ||||
| 			if (cv_reducevfx.value) | ||||
| 			{ | ||||
| 				invinc_rotation_delay = 8; | ||||
| 			} | ||||
| 
 | ||||
| 			boolean updatecolor = false; | ||||
| 			if ((trans >= NUMTRANSMAPS) || mobj->target->player->flamedash || mobj->target->player->tripwirePass < TRIPWIRE_BOOST) | ||||
| 			{ | ||||
| 				// never show for flameshield dash, below tripwire minimum or transparency invalid
 | ||||
| 				mobj->renderflags &= ~RF_TRANSMASK; | ||||
| 				mobj->renderflags |= RF_DONTDRAW; | ||||
| 			} | ||||
| 			else if (myspeed < (tripspeed - basespeed/2)) | ||||
| 			{ | ||||
| 				mobj->renderflags &= ~(RF_TRANSMASK|RF_DONTDRAW); | ||||
| 				if (cv_reducevfx.value) | ||||
| 				{ | ||||
| 					// < 150% make more transparent for reducevfx
 | ||||
| 					mobj->renderflags |= RF_TRANS40; | ||||
| 				} | ||||
| 				else if (leveltime & 1) | ||||
| 				{ | ||||
| 					// < 150% flickering normally
 | ||||
| 					mobj->renderflags |= RF_DONTDRAW; | ||||
| 				} | ||||
| 				updatecolor = true; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				boolean blastermode = (myspeed >= tripspeed) && (mobj->target->player->tripwirePass >= TRIPWIRE_BLASTER); | ||||
|  | @ -8220,11 +8241,29 @@ static boolean P_MobjRegularThink(mobj_t *mobj) | |||
| 				} | ||||
| 				mobj->renderflags |= (mobj->target->renderflags & RF_DONTDRAW); | ||||
| 
 | ||||
| 				updatecolor = true; | ||||
| 
 | ||||
| 				if (blastermode == !(mobj->flags2 & MF2_AMBUSH)) | ||||
| 				{ | ||||
| 					mobj->flags2 ^= MF2_AMBUSH; | ||||
| 					if (blastermode) | ||||
| 					{ | ||||
| 						P_SetMobjState(mobj, (mobj->extravalue1) ? S_TRIPWIREBOOST_BLAST_BOTTOM : S_TRIPWIREBOOST_BLAST_TOP); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						P_SetMobjState(mobj, (mobj->extravalue1) ? S_TRIPWIREBOOST_BOTTOM : S_TRIPWIREBOOST_TOP); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if (updatecolor) | ||||
| 			{ | ||||
| 				if (mobj->target->player->invincibilitytimer > 0) | ||||
| 				{ | ||||
| 					if (mobj->target->player->invincibilitytimer > itemtime+(2*TICRATE)) | ||||
| 					{ | ||||
| 						mobj->color = K_RainbowColor(leveltime / 2); | ||||
| 						mobj->color = K_RainbowColor(leveltime / invinc_rotation_delay); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
|  | @ -8242,19 +8281,6 @@ static boolean P_MobjRegularThink(mobj_t *mobj) | |||
| 					mobj->color = SKINCOLOR_NONE; | ||||
| 					mobj->colorized = false; | ||||
| 				} | ||||
| 
 | ||||
| 				if (blastermode == !(mobj->flags2 & MF2_AMBUSH)) | ||||
| 				{ | ||||
| 					mobj->flags2 ^= MF2_AMBUSH; | ||||
| 					if (blastermode) | ||||
| 					{ | ||||
| 						P_SetMobjState(mobj, (mobj->extravalue1) ? S_TRIPWIREBOOST_BLAST_BOTTOM : S_TRIPWIREBOOST_BLAST_TOP); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						P_SetMobjState(mobj, (mobj->extravalue1) ? S_TRIPWIREBOOST_BOTTOM : S_TRIPWIREBOOST_TOP); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -8321,6 +8347,17 @@ static boolean P_MobjRegularThink(mobj_t *mobj) | |||
| 			P_RemoveMobj(mobj); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		// This invinc mobj flickers intensely, so don't draw it in reducevfx
 | ||||
| 		if (cv_reducevfx.value && (mobj->renderflags & RF_DONTDRAW) == 0) | ||||
| 		{ | ||||
| 			mobj->renderflags |= RF_DONTDRAW; | ||||
| 		} | ||||
| 		if (!cv_reducevfx.value && (mobj->renderflags & RF_DONTDRAW) != 0) | ||||
| 		{ | ||||
| 			mobj->renderflags ^= RF_DONTDRAW; | ||||
| 		} | ||||
| 
 | ||||
| 		P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z); | ||||
| 		break; | ||||
| 	case MT_BRAKEDRIFT: | ||||
|  |  | |||
|  | @ -455,6 +455,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) | |||
| 		WRITEUINT8(save->p, players[i].position); | ||||
| 		WRITEUINT8(save->p, players[i].oldposition); | ||||
| 		WRITEUINT8(save->p, players[i].positiondelay); | ||||
| 		WRITEUINT8(save->p, players[i].leaderpenalty); | ||||
| 		WRITEUINT8(save->p, players[i].teamposition); | ||||
| 		WRITEUINT8(save->p, players[i].teamimportance); | ||||
| 		WRITEUINT32(save->p, players[i].distancetofinish); | ||||
|  | @ -479,6 +480,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) | |||
| 		WRITEUINT8(save->p, players[i].wipeoutslow); | ||||
| 		WRITEUINT8(save->p, players[i].justbumped); | ||||
| 		WRITEUINT8(save->p, players[i].noEbrakeMagnet); | ||||
| 		WRITEUINT8(save->p, players[i].wallSpikeDampen); | ||||
| 		WRITEUINT8(save->p, players[i].tumbleBounces); | ||||
| 		WRITEUINT16(save->p, players[i].tumbleHeight); | ||||
| 		WRITEUINT16(save->p, players[i].stunned); | ||||
|  | @ -1140,6 +1142,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) | |||
| 		players[i].position = READUINT8(save->p); | ||||
| 		players[i].oldposition = READUINT8(save->p); | ||||
| 		players[i].positiondelay = READUINT8(save->p); | ||||
| 		players[i].leaderpenalty = READUINT8(save->p); | ||||
| 		players[i].teamposition = READUINT8(save->p); | ||||
| 		players[i].teamimportance = READUINT8(save->p); | ||||
| 		players[i].distancetofinish = READUINT32(save->p); | ||||
|  | @ -1164,6 +1167,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) | |||
| 		players[i].wipeoutslow = READUINT8(save->p); | ||||
| 		players[i].justbumped = READUINT8(save->p); | ||||
| 		players[i].noEbrakeMagnet = READUINT8(save->p); | ||||
| 		players[i].wallSpikeDampen = READUINT8(save->p); | ||||
| 		players[i].tumbleBounces = READUINT8(save->p); | ||||
| 		players[i].tumbleHeight = READUINT16(save->p); | ||||
| 		players[i].stunned = READUINT16(save->p); | ||||
|  |  | |||
|  | @ -3375,6 +3375,23 @@ static boolean P_CheckLineSideTripWire(line_t *ld, int p) | |||
| 	terrain = K_GetTerrainForTextureNum(sda->midtexture); | ||||
| 	tripwire = terrain && (terrain->flags & TRF_TRIPWIRE); | ||||
| 
 | ||||
| 	// If we are texture TRIPWIRE and have the ML_MIDTEXINVISWALL, the replace texture with TRIPWLOW
 | ||||
| 	if (tripwire && (ld->flags & ML_MIDTEXINVISWALL)) // if we do backwards compat, update this to also swap for older custom maps without the flag
 | ||||
| 	{ | ||||
| 		if (sda->midtexture == R_TextureNumForName("TRIPWIRE")) | ||||
| 		{ | ||||
| 			sda->midtexture = R_TextureNumForName("TRIPWLOW"); | ||||
| 		} | ||||
| 		else if (sda->midtexture == R_TextureNumForName("2RIPWIRE")) | ||||
| 		{ | ||||
| 			sda->midtexture = R_TextureNumForName("2RIPWLOW"); | ||||
| 		} | ||||
| 		else if (sda->midtexture == R_TextureNumForName("4RIPWIRE")) | ||||
| 		{ | ||||
| 			sda->midtexture = R_TextureNumForName("4RIPWLOW"); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (tripwire) | ||||
| 	{ | ||||
| 		// copy midtexture to other side
 | ||||
|  | @ -7841,24 +7858,11 @@ static void P_LoadRecordGhosts(void) | |||
| { | ||||
| 	// see also /menus/play-local-race-time-attack.c's M_PrepareTimeAttack
 | ||||
| 	char *gpath; | ||||
| 	const char *modeprefix = ""; | ||||
| 	const char *modeprefix = M_GetRecordMode(); | ||||
| 	INT32 i; | ||||
| 
 | ||||
| 	gpath = Z_StrDup(va("%s" PATHSEP "media" PATHSEP "replay" PATHSEP "%s" PATHSEP "%s", srb2home, timeattackfolder, G_BuildMapName(gamemap))); | ||||
| 
 | ||||
| 	if (encoremode) | ||||
| 	{ | ||||
| 		modeprefix = "-spb"; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		const INT32 skinid = R_SkinAvailableEx(cv_skin[0].string, false); | ||||
| 		if (skinid >= 0 && (skins[skinid]->flags & SF_HIVOLT)) | ||||
| 		{ | ||||
| 			modeprefix = "-hivolt"; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	enum | ||||
| 	{ | ||||
| 		kTime	= 1 << 0, | ||||
|  | @ -7883,7 +7887,7 @@ static void P_LoadRecordGhosts(void) | |||
| 
 | ||||
| 	auto add_ghosts = [gpath](const srb2::String& base, UINT8 bits) | ||||
| 	{ | ||||
| 		auto load = [base](const char* suffix) { P_TryAddExternalGhost(fmt::format("{}-{}.lmp", base, suffix).c_str()); }; | ||||
| 		auto load = [base](const char* suffix) { P_TryAddExternalGhost(fmt::format("{}{}.lmp", base, suffix).c_str()); }; | ||||
| 
 | ||||
| 		if (bits & kTime) | ||||
| 			load("time-best"); | ||||
|  | @ -7901,7 +7905,7 @@ static void P_LoadRecordGhosts(void) | |||
| 	if (allGhosts) | ||||
| 	{ | ||||
| 		for (i = 0; i < numskins; ++i) | ||||
| 			add_ghosts(fmt::format("{}-{}{}", gpath, skins[i]->name, modeprefix), allGhosts); | ||||
| 			add_ghosts(fmt::format("{}-{}-{}", gpath, skins[i]->name, modeprefix), allGhosts); | ||||
| 	} | ||||
| 
 | ||||
| 	if (sameGhosts) | ||||
|  | @ -7909,7 +7913,7 @@ static void P_LoadRecordGhosts(void) | |||
| 		INT32 skin = R_SkinAvailableEx(cv_skin[0].string, false); | ||||
| 		if (skin < 0 || !R_SkinUsable(-1, skin, false)) | ||||
| 			skin = 0; // use default skin
 | ||||
| 		add_ghosts(fmt::format("{}-{}{}", gpath, skins[skin]->name, modeprefix), sameGhosts); | ||||
| 		add_ghosts(fmt::format("{}-{}-{}", gpath, skins[skin]->name, modeprefix), sameGhosts); | ||||
| 	} | ||||
| 
 | ||||
| 	// Guest ghost
 | ||||
|  | @ -9100,6 +9104,8 @@ void P_PostLoadLevel(void) | |||
| 		marathonmode = static_cast<marathonmode_t>(marathonmode & ~MA_INIT); | ||||
| 	} | ||||
| 
 | ||||
| 	Music_TuneReset(); // Placed before ACS scripts to allow remaps to occur on level start.
 | ||||
| 
 | ||||
| 	ACS_RunLevelStartScripts(); | ||||
| 	LUA_HookInt(gamemap, HOOK(MapLoad)); | ||||
| 
 | ||||
|  | @ -9565,8 +9571,8 @@ void Command_Platinums(void) | |||
| 			else | ||||
| 			{ | ||||
| 				CONS_Printf(", %s (+%d:%02d:%02d)", stafftime.second.c_str(), | ||||
| 					G_TicsToMinutes(stafftime.first - platinumtime, true),  | ||||
| 					G_TicsToSeconds(stafftime.first - platinumtime),  | ||||
| 					G_TicsToMinutes(stafftime.first - platinumtime, true), | ||||
| 					G_TicsToSeconds(stafftime.first - platinumtime), | ||||
| 					G_TicsToCentiseconds(stafftime.first - platinumtime)); | ||||
| 			} | ||||
| 
 | ||||
|  |  | |||
|  | @ -2067,7 +2067,7 @@ static void K_HandleLapIncrement(player_t *player) | |||
| 				K_UpdateAllPlayerPositions(); // P_DoPlayerExit calls this
 | ||||
| 			} | ||||
| 
 | ||||
| 			if (!G_TimeAttackStart() && player->laps == 1 && lapisfresh) | ||||
| 			if (!G_TimeAttackStart() && !(gametyperules & GTR_ROLLINGSTART) && player->laps == 1 && lapisfresh) | ||||
| 			{ | ||||
| 				if (rainbowstartavailable) | ||||
| 				{ | ||||
|  |  | |||
|  | @ -1330,7 +1330,7 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) | |||
| 
 | ||||
| 			if (grandprixinfo.gp == true | ||||
| 				&& grandprixinfo.eventmode != GPEVENT_SPECIAL | ||||
| 				&& player->bot == false && losing == false) | ||||
| 				&& player->bot == false && losing == false && player->hudrings > 0) | ||||
| 			{ | ||||
| 				const UINT8 lifethreshold = 20; | ||||
| 
 | ||||
|  | @ -4647,6 +4647,11 @@ void P_PlayerThink(player_t *player) | |||
| 		player->flashing--; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!player->flashing && !P_PlayerInPain(player)) | ||||
| 	{ | ||||
| 		player->wallSpikeDampen = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (player->nocontrol && player->nocontrol < UINT16_MAX) | ||||
| 	{ | ||||
| 		player->nocontrol--; | ||||
|  |  | |||
|  | @ -429,6 +429,13 @@ boolean R_IsDebugLine(seg_t *line) | |||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| boolean R_ShouldFlipTripWire(const line_t *ld) | ||||
| { | ||||
| 	// Flip tripwire textures when they are unpegged
 | ||||
| 	// so the energy flows downward instead of upward, matching collision behavior
 | ||||
| 	return (ld->tripwire && !(ld->flags & ML_MIDPEG)); | ||||
| } | ||||
| 
 | ||||
| //
 | ||||
| // R_AddLine
 | ||||
| // Clips the given segment and adds any visible pieces to the line list.
 | ||||
|  |  | |||
|  | @ -61,6 +61,7 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, | |||
| 	INT32 *ceilinglightlevel, boolean back); | ||||
| boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back); | ||||
| boolean R_IsDebugLine(seg_t *line); | ||||
| boolean R_ShouldFlipTripWire(const line_t *ld); | ||||
| 
 | ||||
| INT32 R_GetPlaneLight(sector_t *sector, fixed_t planeheight, boolean underside); | ||||
| void R_Prep3DFloors(sector_t *sector); | ||||
|  |  | |||
|  | @ -696,7 +696,7 @@ void R_RenderMaskedSegRange(drawseg_t *drawseg, INT32 x1, INT32 x2) | |||
| 	// are not stored per-column with post info in SRB2
 | ||||
| 	if (textures[texnum]->holes) | ||||
| 	{ | ||||
| 		if (textures[texnum]->flip & 2) // vertically flipped?
 | ||||
| 		if ((textures[texnum]->flip & 2) || R_ShouldFlipTripWire(ldef)) // vertically flipped?
 | ||||
| 		{ | ||||
| 			colfunc_2s = R_DrawFlippedMaskedColumn; | ||||
| 			lengthcol = textures[texnum]->height; | ||||
|  | @ -706,8 +706,16 @@ void R_RenderMaskedSegRange(drawseg_t *drawseg, INT32 x1, INT32 x2) | |||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		colfunc_2s = R_Render2sidedMultiPatchColumn; // render multipatch with no holes (no post_t info)
 | ||||
| 		lengthcol = textures[texnum]->height; | ||||
| 		if (R_ShouldFlipTripWire(ldef)) // Check for tripwire flip even for non-holey textures
 | ||||
| 		{ | ||||
| 			colfunc_2s = R_DrawFlippedMaskedColumn; | ||||
| 			lengthcol = textures[texnum]->height; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			colfunc_2s = R_Render2sidedMultiPatchColumn; // render multipatch with no holes (no post_t info)
 | ||||
| 			lengthcol = textures[texnum]->height; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	maskedtexturecol = drawseg->maskedtexturecol; | ||||
|  |  | |||
|  | @ -358,6 +358,7 @@ boolean I_InitNetwork(void) | |||
| 	if (M_CheckParm("-server") || dedicated) | ||||
| 	{ | ||||
| 		server = true; | ||||
| 		connectedtodedicated = dedicated; | ||||
| 
 | ||||
| 		// If a number of clients (i.e. nodes) is specified, the server will wait for the clients
 | ||||
| 		// to connect before starting.
 | ||||
|  |  | |||
|  | @ -2796,6 +2796,7 @@ void V_DrawStringScaled( | |||
| 					if (nodanceoverride) | ||||
| 					{ | ||||
| 						dance = false; | ||||
| 						cyoff = 0; | ||||
| 					} | ||||
| 				} | ||||
| 				else if (c == V_STRINGDANCE) | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| // it's only for detection of the version the player is using so the MS can alert them of an update.
 | ||||
| // Only set it higher, not lower, obviously.
 | ||||
| // Note that we use this to help keep internal testing in check; this is why v2.0 is not version "2".
 | ||||
| #define MODVERSION 9 | ||||
| #define MODVERSION 12 | ||||
| 
 | ||||
| // Define this as a prerelease version suffix
 | ||||
| #define BETAVERSION "RC6" | ||||
| #define BETAVERSION "RC9" | ||||
|  |  | |||
|  | @ -2535,12 +2535,12 @@ int W_VerifyNMUSlumps(const char *filename, FILE *handle, boolean exit_on_error) | |||
| 		&& stricmp(&filename[strlen(filename) - 4], ".lua")) | ||||
| 		{ | ||||
| 			status = W_VerifyWAD(handle, NMUSlist, false); | ||||
| 
 | ||||
| 			// repair file handle in this specific case
 | ||||
| 			fseek(handle, 0, SEEK_SET); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// repair file handle so we don't have to open a new one
 | ||||
| 	fseek(handle, 0, SEEK_SET); | ||||
| 
 | ||||
| 	if (status == -1) | ||||
| 		W_InitFileError(filename, exit_on_error); | ||||
| 
 | ||||
|  |  | |||
|  | @ -260,6 +260,7 @@ static inline VOID OpenTextConsole(void) | |||
| 	HANDLE ci, co; | ||||
| 	const BOOL tco = M_CheckParm("-console") != 0; | ||||
| 	dedicated = M_CheckParm("-dedicated") != 0; | ||||
| 	connectedtodedicated = dedicated; | ||||
| 	if (!(dedicated || tco)) | ||||
| 		return; | ||||
| 	FreeConsole(); | ||||
|  |  | |||
|  | @ -162,6 +162,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) | |||
| 	boolean completed[MAXPLAYERS]; | ||||
| 	INT32 numplayersingame = 0; | ||||
| 	boolean getmainplayer = false; | ||||
| 	UINT32 topscore = 0, btopemeralds = 0; | ||||
| 
 | ||||
| 	// Initialize variables
 | ||||
| 	if (rankingsmode > 1) | ||||
|  | @ -190,6 +191,27 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) | |||
| 			data.increase[i] = INT16_MIN; | ||||
| 			continue; | ||||
| 		} | ||||
| 		 | ||||
| 		// for getting the proper maximum value for score-to-EXP conversion
 | ||||
| 		if (gametype == GT_BATTLE) | ||||
| 		{ | ||||
| 			if ((&players[i])->roundscore > topscore) | ||||
| 			{ | ||||
| 				topscore = (&players[i])->roundscore; | ||||
| 			} | ||||
| 			if (K_NumEmeralds(&players[i]) > btopemeralds) | ||||
| 			{ | ||||
| 				btopemeralds = K_NumEmeralds(&players[i]); // necessary so non-emerald wins can still get max EXP if no one else is holding more emeralds
 | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		if (K_InRaceDuel() == true) | ||||
| 		{ | ||||
| 			if (((UINT32)(&players[i])->duelscore) > topscore) | ||||
| 			{ | ||||
| 				topscore = (&players[i])->duelscore; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (!rankingsmode) | ||||
| 			data.increase[i] = INT16_MIN; | ||||
|  | @ -294,10 +316,33 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) | |||
| 			if (powertype == PWRLV_DISABLED) | ||||
| 			{ | ||||
| 				UINT8 pointgetters = numplayersingame + spectateGriefed; | ||||
| 				UINT32 scoreconversion = 0; | ||||
| 				UINT32 pscore = 0; | ||||
| 
 | ||||
| 				if (data.pos[data.numplayers] <= pointgetters) | ||||
| 				// accept players that nocontest, but not bots
 | ||||
| 				if (data.pos[data.numplayers] <= pointgetters && | ||||
| 					!((players[i].pflags & PF_NOCONTEST) && players[i].bot)) | ||||
| 				{ | ||||
| 					data.increase[i] = K_CalculateGPRankPoints((&players[i])->exp, data.pos[data.numplayers], pointgetters); | ||||
| 					if (gametype == GT_BATTLE) | ||||
| 					{ | ||||
| 						pscore = (&players[i])->roundscore + K_NumEmeralds(&players[i]); | ||||
| 						scoreconversion = FixedRescale(pscore, 0, topscore + btopemeralds, Easing_Linear, EXP_MIN, EXP_MAX); | ||||
| 						data.increase[i] = K_CalculateGPRankPoints(scoreconversion, data.pos[data.numplayers], pointgetters); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						// For Duel scoring, convert duelscore into EXP.
 | ||||
| 						if (K_InRaceDuel()) | ||||
| 						{ | ||||
| 							pscore = (&players[i])->duelscore; | ||||
| 							scoreconversion = FixedRescale(pscore, 0, topscore, Easing_Linear, EXP_MIN, EXP_MAX); | ||||
| 							data.increase[i] = K_CalculateGPRankPoints(scoreconversion, data.pos[data.numplayers], pointgetters); | ||||
| 						} | ||||
| 						else | ||||
| 						{ | ||||
| 							data.increase[i] = K_CalculateGPRankPoints((&players[i])->exp, data.pos[data.numplayers], pointgetters); | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					if (data.winningteam != TEAM_UNASSIGNED) | ||||
| 					{ | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue