mirror of
				https://github.com/KartKrewDev/RingRacers.git
				synced 2025-10-30 08:01:28 +00:00 
			
		
		
		
	Compare commits
	
		
			391 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 | ||
|   | 7a40d3b79f | ||
|   | a9fdc85b00 | ||
|   | c24588a593 | ||
|   | a91e62029a | ||
|   | 1f3eef72c7 | ||
|   | 2769037787 | ||
|   | 9ecde56ca4 | ||
|   | 781b1a07a8 | ||
|   | 73ff24ff1b | ||
|   | 40968abdc3 | ||
|   | c48500e453 | ||
|   | a35121716f | ||
|   | e15ab7ab0b | ||
|   | a4c7faf34a | ||
|   | 9080d932f5 | ||
|   | fc8e1f5f0c | ||
|   | 4f5e9dbaa3 | ||
|   | 6fb3de555d | ||
|   | 5284dba540 | ||
|   | 02a1a5576a | ||
|   | 1584e8a134 | ||
|   | 5834f45678 | ||
|   | a28fd6c3dd | ||
|   | 44ebf73464 | ||
|   | 36a0331c8b | ||
|   | 9a2b054ca9 | ||
|   | 4d5099070b | ||
|   | 1c8bf6987d | ||
|   | 02a889c265 | ||
|   | 2f19edabec | ||
|   | 81f54b2ef3 | ||
|   | 6cd9746c4b | ||
|   | 2c4c94d80d | ||
|   | c8c56acd65 | ||
|   | 58b158ffab | ||
|   | 4ea782c493 | ||
|   | 0bcadb7ffe | ||
|   | 45a2470479 | ||
|   | 3f4476ea77 | ||
|   | 22c145ac28 | ||
|   | 0414617e92 | ||
|   | c79b8ea9ba | ||
|   | d143128305 | ||
|   | a3c64581cf | ||
|   | a081f72f6d | ||
|   | 5f76a74c37 | ||
|   | 78e4943ff9 | ||
|   | fb63ce7304 | ||
|   | 98d90e3501 | ||
|   | 225c34d2dc | ||
|   | 5b1e7197c2 | ||
|   | 61d9722214 | ||
|   | 668a2d8d31 | ||
|   | ee8ec81da4 | ||
|   | 81135614ff | ||
|   | 0996610995 | ||
|   | 9e73292a67 | ||
|   | 644ead272b | ||
|   | 9e0920f45e | ||
|   | 0da0cf4909 | ||
|   | 8ed4c1c081 | ||
|   | d883e54886 | ||
|   | f7e819079c | ||
|   | f18022e9c8 | ||
|   | f5e8f24a3c | ||
|   | 499fccf061 | ||
|   | 89df4da48c | ||
|   | c90947e3ce | ||
|   | 7f8b5ec43e | ||
|   | e134b6609f | ||
|   | 6b95e3e218 | ||
|   | cb9e074bda | ||
|   | 1b3012604c | ||
|   | 4d4738487a | ||
|   | 7d6289817d | ||
|   | 43b5117416 | ||
|   | b2d668f015 | ||
|   | 2551fd3751 | ||
|   | f692f5f9e7 | ||
|   | ca68ddd35b | ||
|   | 3802f61dc1 | ||
|   | 1d19eb7210 | ||
|   | 9921d0b427 | ||
|   | 6780b5f607 | ||
|   | 0369bc01fd | ||
|   | 494f4c1641 | ||
|   | 48e60648f6 | ||
|   | e64ff44a39 | ||
|   | 5c9730bec8 | ||
|   | cb3c370e33 | ||
|   | 2b075407a2 | ||
|   | 0418507f5a | ||
|   | 6c980807c3 | ||
|   | 975b2cce98 | ||
|   | 8744023a87 | ||
|   | 6aee524ace | ||
|   | 046564eece | ||
|   | c7c8ee3c7a | ||
|   | 341c62cc07 | ||
|   | f0df4990ae | ||
|   | 9ba4cccc5a | ||
|   | 2989d3564e | ||
|   | 92c117d186 | ||
|   | c8f986db4d | ||
|   | 0734ffc201 | ||
|   | fa787c4e8f | ||
|   | 511d9b083b | ||
|   | 6f61cfaefa | ||
|   | 932deb0b3f | ||
|   | 2309d11274 | ||
|   | a9fe850eda | ||
|   | 210ec5ee63 | ||
|   | cbb99ca79b | ||
|   | 4ff1f38877 | ||
|   | 78b11e8306 | ||
|   | eb90b28c12 | ||
|   | 8bf15ef6f1 | ||
|   | ef4ef91fed | ||
|   | abbc6b03d4 | ||
|   | 50f007b653 | ||
|   | 11acb78875 | ||
|   | 8363ead346 | ||
|   | 32d03ccb1e | ||
|   | d5c4534d31 | ||
|   | 2d3e7ce049 | ||
|   | 14201c65aa | ||
|   | 093cdb1043 | ||
|   | 350cd423e4 | ||
|   | 1d1e7d1741 | ||
|   | 59c4cfb2be | ||
|   | d3ac1a6c5e | ||
|   | 60e65133e6 | ||
|   | 0badaed11d | ||
|   | 53d71225b8 | ||
|   | 80c0db9546 | ||
|   | 06626c9b0d | ||
|   | 0e46138a14 | ||
|   | ae0100ba05 | ||
|   | ab39175430 | ||
|   | 9bdd01f5a6 | ||
|   | 69e23ad50a | ||
|   | e3067e8a09 | ||
|   | 3d16ecd7e9 | ||
|   | bf901fddb3 | ||
|   | 140f9846aa | ||
|   | 9297e3cdcb | ||
|   | 7b10e4e2bd | ||
|   | 667ad18339 | ||
|   | 5e44040ae8 | ||
|   | 55192eaf39 | ||
|   | d157076326 | ||
|   | 8b86622218 | ||
|   | 5ead1e5964 | ||
|   | d5fc805cbd | ||
|   | fbe2f9fd56 | ||
|   | 7b4cf9a07c | ||
|   | d060df9bfe | ||
|   | a05a82de7b | ||
|   | cd038addd5 | ||
|   | 8f4a698a35 | ||
|   | e7ee7ec9ce | ||
|   | 8b71cd9e82 | ||
|   | 3f8ee05db8 | ||
|   | d55eab9748 | ||
|   | 997f995ce9 | ||
|   | bd4b3336bf | ||
|   | a428811884 | ||
|   | 74ae7abd4c | ||
|   | 8649a66e08 | ||
|   | 0c3906690d | ||
|   | 8e4b690387 | ||
|   | cfd8260762 | ||
|   | f2ab528062 | ||
|   | bf330bafcc | ||
|   | 48e1ed5779 | ||
|   | 5950298ac6 | ||
|   | 77768b5a68 | ||
|   | c1a80e4b1b | ||
|   | 65cf6edb93 | ||
|   | b746b1a0e4 | ||
|   | b587b4b574 | ||
|   | ddd48b72d7 | ||
|   | 94ef03dcd2 | ||
|   | a6f27a39e3 | ||
|   | 22e7b5e8ad | ||
|   | a31b4a2682 | ||
|   | c96e3b3cca | ||
|   | 766f34d4f5 | ||
|   | b7f8afd5ef | ||
|   | 6507984c22 | ||
|   | 3cc58d93f2 | ||
|   | 39c2779fb2 | ||
|   | c1b22c0c73 | ||
|   | dc1c3bec0f | ||
|   | e9906915b0 | ||
|   | 8f213c7e1b | ||
|   | cef307816d | ||
|   | d69e667835 | ||
|   | b52e1cb61f | ||
|   | fc4a2e9f80 | ||
|   | 01bcf6116e | ||
|   | 15bac24b8d | ||
|   | c7fcf34a8a | ||
|   | d5f131dcc2 | ||
|   | 2be14316cf | ||
|   | 60fca5210d | ||
|   | 7109cd8c13 | ||
|   | e0fd5b43c5 | ||
|   | 51cc299e40 | ||
|   | 84917e36f1 | ||
|   | 5d30a46728 | ||
|   | d0ef4cdfaf | ||
|   | 220f8b035d | ||
|   | 51e22892ac | ||
|   | eb90dcd047 | ||
|   | 4337618342 | ||
|   | 2a72e0a855 | ||
|   | 6bd6c91077 | ||
|   | aee332fa9b | ||
|   | f7bc6e303e | ||
|   | 4aa3309885 | ||
|   | 595874f4e5 | ||
|   | 3f5f9825a8 | ||
|   | 4478fce917 | ||
|   | f78ae582d4 | ||
|   | 55b333e01f | ||
|   | 867dfb49f2 | ||
|   | 3d5f0e9d73 | ||
|   | 587eddeb70 | ||
|   | 4b94f06186 | ||
|   | 38be6953e7 | ||
|   | a13735822e | ||
|   | b3a2cc2ea9 | ||
|   | 0373a23d72 | ||
|   | 27b9a46f0c | ||
|   | 944c13993b | ||
|   | 0dad1aa918 | ||
|   | f674778097 | ||
|   | 9411ce5387 | ||
|   | a01300cd54 | ||
|   | 1517ba4d89 | ||
|   | 4b81be037a | ||
|   | 75fe652dd5 | ||
|   | daa6dbd667 | 
					 133 changed files with 6004 additions and 1079 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.) | ||||
|  |  | |||
|  | @ -125,6 +125,9 @@ find_package(PNG REQUIRED) | |||
| find_package(SDL2 CONFIG REQUIRED) | ||||
| find_package(CURL REQUIRED) | ||||
| find_package(Opus REQUIRED) | ||||
| if(WIN32 AND NOT MINGW) | ||||
| 	find_package(cpptrace CONFIG REQUIRED) | ||||
| endif() | ||||
| # Use the one in thirdparty/fmt to guarantee a minimum version | ||||
| #find_package(FMT CONFIG REQUIRED) | ||||
| 
 | ||||
|  |  | |||
|  | @ -54,8 +54,8 @@ | |||
| 			"name": "__develop-cl", | ||||
| 			"hidden": true, | ||||
| 			"cacheVariables": { | ||||
| 				"CMAKE_C_FLAGS_RELWITHDEBINFO": "/MD /O2 /Ob2 /DNDEBUG", | ||||
| 				"CMAKE_CXX_FLAGS_RELWITHDEBINFO": "/MD /O2 /Ob2 /DNDEBUG", | ||||
| 				"CMAKE_C_FLAGS_RELWITHDEBINFO": "/MD /Zi /O2 /Ob2 /DNDEBUG", | ||||
| 				"CMAKE_CXX_FLAGS_RELWITHDEBINFO": "/MD /Zi /O2 /Ob2 /DNDEBUG", | ||||
| 				"SRB2_CONFIG_DEV_BUILD": "ON", | ||||
| 				"CMAKE_BUILD_TYPE": "RelWithDebInfo" | ||||
| 			} | ||||
|  | @ -64,8 +64,8 @@ | |||
| 			"name": "__release-cl", | ||||
| 			"hidden": true, | ||||
| 			"cacheVariables": { | ||||
| 				"CMAKE_C_FLAGS_RELWITHDEBINFO": "/MD /O2 /Ob2 /DNDEBUG", | ||||
| 				"CMAKE_CXX_FLAGS_RELWITHDEBINFO": "/MD /O2 /Ob2 /DNDEBUG", | ||||
| 				"CMAKE_C_FLAGS_RELWITHDEBINFO": "/MD /Zi /O2 /Ob2 /DNDEBUG", | ||||
| 				"CMAKE_CXX_FLAGS_RELWITHDEBINFO": "/MD /Zi /O2 /Ob2 /DNDEBUG", | ||||
| 				"SRB2_CONFIG_DEV_BUILD": "OFF", | ||||
| 				"CMAKE_BUILD_TYPE": "RelWithDebInfo" | ||||
| 			} | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| include(CheckCXXCompilerFlag) | ||||
| 
 | ||||
| add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 | ||||
| 	comptime.c | ||||
| 	cxxutil.hpp | ||||
|  | @ -128,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 | ||||
|  | @ -176,6 +179,41 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 | |||
| 	p_deepcopy.cpp | ||||
| ) | ||||
| 
 | ||||
| # Always compile some files with optimizations in Debug config. | ||||
| # This is to make the debugging experience less slow for the majority of cases. | ||||
| set(always_optimize_options "") | ||||
| if(MSVC) | ||||
| 	set(always_optimize_options "$<$<CONFIG:Debug>:/O2;/Ob2>") | ||||
| else() | ||||
| 	set(always_optimize_options "$<$<CONFIG:Debug>:-O3>") | ||||
| endif() | ||||
| set_source_files_properties( | ||||
| 	r_bsp.cpp | ||||
| 	r_data.c | ||||
| 	r_debug.cpp | ||||
| 	r_debug_parser.cpp | ||||
| 	r_debug_printer.cpp | ||||
| 	r_draw.cpp | ||||
| 	r_fps.cpp | ||||
| 	r_main.cpp | ||||
| 	r_plane.cpp | ||||
| 	r_segs.cpp | ||||
| 	r_skins.c | ||||
| 	r_sky.c | ||||
| 	r_splats.c | ||||
| 	r_spritefx.cpp | ||||
| 	r_things.cpp | ||||
| 	r_bbox.c | ||||
| 	r_textures.cpp | ||||
| 	r_textures_dups.cpp | ||||
| 	r_patch.cpp | ||||
| 	r_patchrotation.c | ||||
| 	r_picformats.c | ||||
| 	r_portal.c | ||||
| 	PROPERTIES | ||||
| 	COMPILE_OPTIONS "${always_optimize_options}" | ||||
| ) | ||||
| 
 | ||||
| set_target_properties(SRB2SDL2 PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/sdl/macosx/MacOSXBundleInfo.plist.in") | ||||
| 
 | ||||
| if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) | ||||
|  | @ -210,7 +248,7 @@ add_custom_target(_SRB2_reconf ALL | |||
| ) | ||||
| add_dependencies(SRB2SDL2 _SRB2_reconf) | ||||
| 
 | ||||
| if(("${CMAKE_COMPILER_IS_GNUCC}" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows") | ||||
| if(("${CMAKE_COMPILER_IS_GNUCC}") AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows") | ||||
| 	target_link_options(SRB2SDL2 PRIVATE "-Wl,--disable-dynamicbase") | ||||
| 	if("${SRB2_CONFIG_STATIC_STDLIB}") | ||||
| 		# On MinGW with internal libraries, link the standard library statically | ||||
|  | @ -224,8 +262,19 @@ if(("${CMAKE_COMPILER_IS_GNUCC}" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND | |||
| 	endif() | ||||
| endif() | ||||
| 
 | ||||
| if(MSVC OR CMAKE_CXX_LINK_EXECUTABLE MATCHES "lld-link.exe") | ||||
| if(MSVC OR CMAKE_CXX_LINK_EXECUTABLE MATCHES "lld-link\\.exe") | ||||
| 	target_link_options(SRB2SDL2 PRIVATE /MANIFEST:NO) | ||||
| 	target_link_libraries(SRB2SDL2 PRIVATE "dbghelp") | ||||
| 
 | ||||
| endif() | ||||
| 
 | ||||
| if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND MSVC) | ||||
| 	# Stuff to make paths not include the builder's file system paths on clang-cl | ||||
| 	string(REPLACE "/" "\\" backslashes_cmake_source_dir "${CMAKE_SOURCE_DIR}") | ||||
| 	target_compile_options(SRB2SDL2 PRIVATE "$<$<CONFIG:RelWithDebInfo,Release>:/clang:-fdebug-prefix-map=${CMAKE_SOURCE_DIR}=X:/ringracers>") | ||||
| 	target_compile_options(SRB2SDL2 PRIVATE "$<$<CONFIG:RelWithDebInfo,Release>:/clang:-fdebug-prefix-map=${backslashes_cmake_source_dir}=X:/ringracers>") | ||||
| 	target_compile_options(SRB2SDL2 PRIVATE "$<$<CONFIG:RelWithDebInfo,Release>:/clang:-fdebug-compilation-dir=${CMAKE_SOURCE_DIR}>") | ||||
| 	target_link_options(SRB2SDL2 PRIVATE "$<$<CONFIG:RelWithDebInfo,Release>:/pdbaltpath:%_PDB%>") | ||||
| endif() | ||||
| 
 | ||||
| target_compile_features(SRB2SDL2 PRIVATE c_std_11 cxx_std_17) | ||||
|  | @ -279,10 +328,35 @@ if("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") | |||
| 	target_compile_definitions(SRB2SDL2 PRIVATE -DMACOSX) | ||||
| endif() | ||||
| 
 | ||||
| # Enable SSE2 for IA-32. | ||||
| if(SRB2_SYSTEM_BITS EQUAL 32) | ||||
| 	if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang|AppleClang)") | ||||
| 		check_cxx_compiler_flag("-msse2" has_sse2) | ||||
| 		if(has_sse2) | ||||
| 			message(STATUS "Using SSE2 on IA-32") | ||||
| 			target_compile_options(SRB2SDL2 PRIVATE "-msse2") | ||||
| 		else() | ||||
| 			message(FATAL_ERROR "Needs -msse2 compiler support") | ||||
| 		endif() | ||||
| 	elseif(MSVC) | ||||
| 		check_cxx_compiler_flag("/arch:sse2" has_sse2) | ||||
| 		if(has_sse2) | ||||
| 			message(STATUS "Using SSE2 on IA-32") | ||||
| 			target_compile_options(SRB2SDL2 PRIVATE "/arch:SSE2") | ||||
| 		else() | ||||
| 			message(FATAL_ERROR "Needs /arch:SSE2 compiler support") | ||||
| 		endif() | ||||
| 	endif() | ||||
| endif() | ||||
| 
 | ||||
| target_link_libraries(SRB2SDL2 PRIVATE ZLIB::ZLIB) | ||||
| target_link_libraries(SRB2SDL2 PRIVATE PNG::PNG) | ||||
| target_link_libraries(SRB2SDL2 PRIVATE CURL::libcurl) | ||||
| target_link_libraries(SRB2SDL2 PRIVATE Opus::opus) | ||||
| if(WIN32 AND NOT MINGW) | ||||
| 	target_link_libraries(SRB2SDL2 PRIVATE cpptrace::cpptrace) | ||||
| 	target_compile_definitions(SRB2SDL2 PRIVATE HAVE_CPPTRACE) | ||||
| endif() | ||||
| target_link_libraries(SRB2SDL2 PRIVATE ReNameNoise::renamenoise) | ||||
| if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") | ||||
| 	target_link_libraries(SRB2SDL2 PRIVATE -lexecinfo) | ||||
|  | @ -461,6 +535,7 @@ target_compile_options(SRB2SDL2 PRIVATE | |||
| 	# This is a direct translation from versions.mk | ||||
| 	$<$<AND:$<COMPILE_LANGUAGE:C>,$<C_COMPILER_ID:GNU>>: | ||||
| 		-Wall | ||||
| 		-Werror-implicit-function-declaration | ||||
| 		-Wno-trigraphs | ||||
| 		-W # Was controlled by RELAXWARNINGS | ||||
| 		-pedantic | ||||
|  | @ -545,6 +620,7 @@ target_compile_options(SRB2SDL2 PRIVATE | |||
| 	$<$<AND:$<COMPILE_LANGUAGE:C>,$<OR:$<C_COMPILER_ID:AppleClang>,$<C_COMPILER_ID:Clang>>>: | ||||
| 		#-Wall | ||||
| 		-Werror=microsoft | ||||
| 		-Werror=implicit-function-declaration | ||||
| 		-Wreturn-type # Missing returns in non-void function | ||||
| 		-Wduplicate-decl-specifier | ||||
| 		-Wsometimes-uninitialized | ||||
|  | @ -570,9 +646,11 @@ target_compile_options(SRB2SDL2 PRIVATE | |||
| 	# C++, GNU | ||||
| 	$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:GNU>>: | ||||
| 		-Wall | ||||
| 		-Wno-c++11-compat | ||||
| 		-Wno-c++14-compat # No C++14 compat needed | ||||
| 	> | ||||
| 
 | ||||
| 	# C++, GNU, Clang and Apple Clang | ||||
| 	# C++, Clang and Apple Clang | ||||
| 	$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<OR:$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:Clang>>>: | ||||
| 		#-Wall | ||||
| 		-Werror=microsoft | ||||
|  | @ -584,6 +662,8 @@ target_compile_options(SRB2SDL2 PRIVATE | |||
| 		-Wno-c++98-compat | ||||
| 		-Wno-c++11-compat | ||||
| 		-Wno-c++14-compat # No C++14 compat needed | ||||
| 		-Werror=c++20-extensions | ||||
| 		-Werror=c++23-extensions # Disallow newer language features entirely | ||||
| 		-Wno-unused-but-set-variable # Setting unread variables is fine (nontrivial C++ types issue) | ||||
| 		-Wno-misleading-indentation # Some cases in code currently | ||||
| 		-Wno-deprecated-non-prototype # We have no intention of using C23 yet. | ||||
|  | @ -594,6 +674,8 @@ target_compile_options(SRB2SDL2 PRIVATE | |||
| 		-Wno-unused-function | ||||
| 	> | ||||
| 
 | ||||
| 	# C++, Clang and Apple Clang | ||||
| 
 | ||||
| 	# C++, MSVC | ||||
| 	$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>: | ||||
| 		/Wv:19.20.27004.0 | ||||
|  |  | |||
|  | @ -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)); | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ | |||
| #include "Script.hpp" | ||||
| #include "Serial.hpp" | ||||
| #include "Thread.hpp" | ||||
| #include "../../../w_wad.h" | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <list> | ||||
|  | @ -596,7 +597,12 @@ namespace ACSVM | |||
|    ModuleName Environment::readModuleName(Serial &in) const | ||||
|    { | ||||
|       auto s = readString(in); | ||||
|       auto i = ReadVLN<std::size_t>(in); | ||||
|       size_t i = ReadVLN<std::size_t>(in); | ||||
| 
 | ||||
|       if ((i = W_LumpFromNetSave(i)) == LUMPERROR) | ||||
|       { | ||||
|           CONS_Debug(DBG_GAMELOGIC, "lumpnum not found for ACS module '%s'\n", s->str); | ||||
|       } | ||||
| 
 | ||||
|       return {s, nullptr, i}; | ||||
|    } | ||||
|  | @ -768,7 +774,7 @@ namespace ACSVM | |||
|    void Environment::writeModuleName(Serial &out, ModuleName const &in) const | ||||
|    { | ||||
|       writeString(out, in.s); | ||||
|       WriteVLN(out, in.i); | ||||
|       WriteVLN<std::size_t>(out, W_LumpIntoNetSave(in.i)); | ||||
|    } | ||||
| 
 | ||||
|    //
 | ||||
|  |  | |||
|  | @ -113,6 +113,7 @@ CV_PossibleValue_t descriptiveinput_cons_t[] = { | |||
| 	{7, "6Bt. (D)"}, | ||||
| 	{8, "6Bt. (E)"}, | ||||
| 	{9, "6Bt. (F)"}, | ||||
| 	{10, "6Bt. (G)"}, | ||||
| 	{0, NULL} | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -420,7 +420,11 @@ consvar_t cv_bgaudio = Player("bgaudio", "Nothing").onchange_noinit(BGAudio_OnCh | |||
| 	{0, "Nothing"}, | ||||
| 	{1, "Music"}, | ||||
| 	{2, "Sounds"}, | ||||
| 	{4, "Voices"}, | ||||
| 	{3, "Music&Sounds"}, | ||||
| 	{5, "Music&Voices"}, | ||||
| 	{6, "Sounds&Voices"}, | ||||
| 	{7, "MusicSounds&Voices"} | ||||
| }); | ||||
| 
 | ||||
| // Pause game upon window losing focus
 | ||||
|  | @ -829,6 +833,7 @@ void ForceSkin_OnChange(void); | |||
| consvar_t cv_forceskin = OnlineCheat("forcecharacter", "None").onchange(ForceSkin_OnChange).description("Force all players to use one character"); | ||||
| 
 | ||||
| consvar_t cv_fuzz = OnlineCheat("fuzz", "Off").on_off().description("Human players spam random inputs, get random items"); | ||||
| consvar_t cv_skiprender = OnlineCheat("skiprender", "1").values(CV_Natural).description("Only render every X frames"); | ||||
| 
 | ||||
| consvar_t cv_kartdebugamount = OnlineCheat("debugitemamount", "1").min_max(1, 255).description("If debugitem, give multiple copies of an item"); | ||||
| consvar_t cv_kartdebugbots = OnlineCheat("debugbots", "Off").on_off().description("Bot AI debugger"); | ||||
|  | @ -1003,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"}}); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										173
									
								
								src/d_clisrv.c
									
										
									
									
									
								
							
							
						
						
									
										173
									
								
								src/d_clisrv.c
									
										
									
									
									
								
							|  | @ -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)
 | ||||
| 
 | ||||
|  | @ -206,8 +207,11 @@ static UINT32 g_player_voice_frames_this_tic[MAXPLAYERS]; | |||
| static OpusEncoder *g_local_opus_encoder; | ||||
| static ReNameNoiseDenoiseState *g_local_renamenoise_state; | ||||
| static UINT64 g_local_opus_frame = 0; | ||||
| #define SRB2_VOICE_OPUS_FRAME_SIZE 960 | ||||
| static float g_local_voice_buffer[SRB2_VOICE_OPUS_FRAME_SIZE]; | ||||
| #define SRB2_VOICE_OPUS_FRAME_SIZE (20 * 48) | ||||
| #define SRB2_VOICE_MAX_FRAMES 8 | ||||
| #define SRB2_VOICE_MAX_DEQUEUE_SAMPLES (SRB2_VOICE_MAX_FRAMES * SRB2_VOICE_OPUS_FRAME_SIZE) | ||||
| #define SRB2_VOICE_MAX_DEQUEUE_BYTES (SRB2_VOICE_MAX_DEQUEUE_SAMPLES * sizeof(float)) | ||||
| static float g_local_voice_buffer[SRB2_VOICE_MAX_DEQUEUE_SAMPLES]; | ||||
| static INT32 g_local_voice_buffer_len = 0; | ||||
| static INT32 g_local_voice_threshold_time = 0; | ||||
| float g_local_voice_last_peak = 0; | ||||
|  | @ -1268,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; | ||||
|  | @ -2244,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) | ||||
| 			{ | ||||
|  | @ -2499,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) | ||||
|  | @ -2726,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) | ||||
|  | @ -2745,6 +2764,7 @@ void CL_Reset(void) | |||
| 	multiplayer = false; | ||||
| 	servernode = 0; | ||||
| 	server = true; | ||||
| 	connectedtodedicated = false; | ||||
| 	doomcom->numnodes = 1; | ||||
| 	doomcom->numslots = 1; | ||||
| 	SV_StopServer(); | ||||
|  | @ -3742,8 +3762,6 @@ static void InitializeLocalVoiceEncoder(void) | |||
| 		CONS_Alert(CONS_WARNING, "Failed to create Opus voice encoder: opus error %d\n", error); | ||||
| 		encoder = NULL; | ||||
| 	} | ||||
| 	opus_encoder_ctl(encoder, OPUS_SET_VBR(0)); | ||||
| 	opus_encoder_ctl(encoder, OPUS_SET_BITRATE(28000)); | ||||
| 	g_local_opus_encoder = encoder; | ||||
| 	g_local_opus_frame = 0; | ||||
| } | ||||
|  | @ -3811,6 +3829,12 @@ static void Got_AddPlayer(const UINT8 **p, INT32 playernum) | |||
| 		newplayer->availabilities[i] = READUINT8(*p); | ||||
| 	} | ||||
| 
 | ||||
| 	if (server) | ||||
| 	{ | ||||
| 		for (i = 0; i < G_LocalSplitscreenPartySize(newplayernum); ++i) | ||||
| 			playerdelaytable[G_LocalSplitscreenPartyMember(newplayernum, i)] = mindelay; | ||||
| 	} | ||||
| 
 | ||||
| 	// the server is creating my player
 | ||||
| 	if (node == mynode) | ||||
| 	{ | ||||
|  | @ -3838,12 +3862,6 @@ static void Got_AddPlayer(const UINT8 **p, INT32 playernum) | |||
| 
 | ||||
| 		P_ForceLocalAngle(newplayer, newplayer->angleturn); | ||||
| 		addedtogame = true; | ||||
| 
 | ||||
| 		if (server) | ||||
| 		{ | ||||
| 			for (i = 0; i < G_LocalSplitscreenPartySize(newplayernum); ++i) | ||||
| 				playerdelaytable[G_LocalSplitscreenPartyMember(newplayernum, i)] = mindelay; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	newplayer->splitscreenindex = splitscreenplayer; | ||||
|  | @ -4260,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 | ||||
|  | @ -4362,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....)
 | ||||
| 
 | ||||
|  | @ -4988,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); | ||||
| 
 | ||||
|  | @ -5379,7 +5404,7 @@ static void PT_HandleVoiceClient(SINT8 node, boolean isserver) | |||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	float *decoded_out = Z_Malloc(sizeof(float) * SRB2_VOICE_OPUS_FRAME_SIZE, PU_STATIC, NULL); | ||||
| 	float *decoded_out = Z_Malloc(sizeof(float) * 1920, PU_STATIC, NULL); | ||||
| 
 | ||||
| 	INT32 decoded_samples = 0; | ||||
| 	UINT64 missedframes = 0; | ||||
|  | @ -5390,26 +5415,26 @@ static void PT_HandleVoiceClient(SINT8 node, boolean isserver) | |||
| 
 | ||||
| 	for (UINT64 i = 0; i < missedframes; i++) | ||||
| 	{ | ||||
| 		decoded_samples = opus_decode_float(decoder, NULL, 0, decoded_out, SRB2_VOICE_OPUS_FRAME_SIZE, 0); | ||||
| 		decoded_samples = opus_decode_float(decoder, NULL, 0, decoded_out, 1920, 0); | ||||
| 		if (decoded_samples < 0) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (cv_voice_selfdeafen.value != 1 && playernum != g_localplayers[0]) | ||||
| 		if (cv_voice_selfdeafen.value != 1 && playernum != g_localplayers[0] && !g_voice_disabled) | ||||
| 		{ | ||||
| 			S_QueueVoiceFrameFromPlayer(playernum, (void*)decoded_out, decoded_samples * sizeof(float), false); | ||||
| 		} | ||||
| 	} | ||||
| 	g_player_opus_lastframe[playernum] = framenum; | ||||
| 
 | ||||
| 	decoded_samples = opus_decode_float(decoder, frame, framesize, decoded_out, SRB2_VOICE_OPUS_FRAME_SIZE, 0); | ||||
| 	decoded_samples = opus_decode_float(decoder, frame, framesize, decoded_out, 1920, 0); | ||||
| 	if (decoded_samples < 0) | ||||
| 	{ | ||||
| 		Z_Free(decoded_out); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (cv_voice_selfdeafen.value != 1 && playernum != g_localplayers[0]) | ||||
| 	if (cv_voice_selfdeafen.value != 1 && playernum != g_localplayers[0] && !g_voice_disabled) | ||||
| 	{ | ||||
| 		S_QueueVoiceFrameFromPlayer(playernum, (void*)decoded_out, decoded_samples * sizeof(float), terminal); | ||||
| 	} | ||||
|  | @ -7378,7 +7403,9 @@ void NetKeepAlive(void) | |||
| 	FileSendTicker(); | ||||
| 
 | ||||
| 	// Update voice whenever possible.
 | ||||
| 	NetVoiceUpdate(); | ||||
| 	{ | ||||
| 		NetVoiceUpdate(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // If a tree falls in the forest but nobody is around to hear it, does it make a tic?
 | ||||
|  | @ -7566,52 +7593,49 @@ void NetUpdate(void) | |||
| 	FileSendTicker(); | ||||
| } | ||||
| 
 | ||||
| static INT32 BiggestOpusFrameLength(INT32 samples) | ||||
| { | ||||
| 	if (samples >= 1920) return 1920; | ||||
| 	if (samples >= 960) return 960; | ||||
| 	if (samples >= 480) return 480; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void NetVoiceUpdate(void) | ||||
| { | ||||
| 	UINT8 *encoded = NULL; | ||||
| 	float *subframe_buffer = NULL; | ||||
| 	float *denoise_buffer = NULL; | ||||
| 	ps_voiceupdatetime = I_GetPreciseTime(); | ||||
| 
 | ||||
| 	if (dedicated) | ||||
| 	{ | ||||
| 		ps_voiceupdatetime = I_GetPreciseTime() - ps_voiceupdatetime; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	floatdenormalstate_t dnzstate = M_EnterFloatDenormalToZero(); | ||||
| 
 | ||||
| 	UINT32 bytes_dequed = 0; | ||||
| 	do | ||||
| 
 | ||||
| 	bytes_dequed = S_SoundInputDequeueSamples((void*)(g_local_voice_buffer + g_local_voice_buffer_len), SRB2_VOICE_MAX_DEQUEUE_BYTES - (g_local_voice_buffer_len * sizeof(float))); | ||||
| 	g_local_voice_buffer_len += bytes_dequed / 4; | ||||
| 
 | ||||
| 	INT32 buffer_offset = 0; | ||||
| 	INT32 frame_length = 0; | ||||
| 	for ( | ||||
| 		; | ||||
| 		(frame_length = BiggestOpusFrameLength(g_local_voice_buffer_len - buffer_offset)) > 0 && (buffer_offset + frame_length) < g_local_voice_buffer_len; | ||||
| 		buffer_offset += frame_length | ||||
| 	) | ||||
| 	{ | ||||
| 		// We need to drain the input queue completely, so do this in a full loop
 | ||||
| 
 | ||||
| 		UINT32 to_read = (SRB2_VOICE_OPUS_FRAME_SIZE - g_local_voice_buffer_len) * sizeof(float); | ||||
| 
 | ||||
| 		if (to_read > 0) | ||||
| 		{ | ||||
| 			// Attempt to fill the voice frame buffer
 | ||||
| 
 | ||||
| 			bytes_dequed = S_SoundInputDequeueSamples((void*)(g_local_voice_buffer + g_local_voice_buffer_len), to_read); | ||||
| 			g_local_voice_buffer_len += bytes_dequed / 4; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			bytes_dequed = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		if (g_local_voice_buffer_len < SRB2_VOICE_OPUS_FRAME_SIZE) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (S_SoundInputRemainingSamples() > 5 * SRB2_VOICE_OPUS_FRAME_SIZE) | ||||
| 		{ | ||||
| 			// If there are too many frames worth of samples to dequeue (100ms), skip this frame instead of encoding.
 | ||||
| 			// This is so we drain the queue without sending too many packets that might queue up on the network driver.
 | ||||
| 			g_local_voice_buffer_len = 0; | ||||
| 			continue; | ||||
| 		} | ||||
| 		float *frame_buffer = g_local_voice_buffer + buffer_offset; | ||||
| 
 | ||||
| 		// Amp of +10 dB is appromiately "twice as loud"
 | ||||
| 		float ampfactor = powf(10, (float) cv_voice_inputamp.value / 20.f); | ||||
| 		for (int i = 0; i < g_local_voice_buffer_len; i++) | ||||
| 		for (int i = 0; i < frame_length; i++) | ||||
| 		{ | ||||
| 			g_local_voice_buffer[i] *= ampfactor; | ||||
| 			frame_buffer[i] *= ampfactor; | ||||
| 		} | ||||
| 
 | ||||
| 		if (cv_voice_denoise.value) | ||||
|  | @ -7620,30 +7644,34 @@ void NetVoiceUpdate(void) | |||
| 			{ | ||||
| 				InitializeLocalVoiceDenoiser(); | ||||
| 			} | ||||
| 			int rnnoise_size = renamenoise_get_frame_size(); | ||||
| 			float *subframe_buffer = (float*) Z_Malloc(rnnoise_size * sizeof(float), PU_STATIC, NULL); | ||||
| 			float *denoise_buffer = (float*) Z_Malloc(rnnoise_size * sizeof(float), PU_STATIC, NULL); | ||||
| 			int rnnoise_size = renamenoise_get_frame_size(); // this is always 480
 | ||||
| 			if (subframe_buffer == NULL) | ||||
| 			{ | ||||
| 				subframe_buffer = (float*) Z_Malloc(rnnoise_size * sizeof(float), PU_STATIC, NULL); | ||||
| 			} | ||||
| 			if (denoise_buffer == NULL) | ||||
| 			{ | ||||
| 				denoise_buffer = (float*) Z_Malloc(rnnoise_size * sizeof(float), PU_STATIC, NULL); | ||||
| 			} | ||||
| 
 | ||||
| 			// rnnoise frames are smaller than opus, but we should not expect the opus frame to be an exact multiple of rnnoise
 | ||||
| 			for (int denoise_position = 0; denoise_position < SRB2_VOICE_OPUS_FRAME_SIZE; denoise_position += rnnoise_size) | ||||
| 			for (int denoise_position = 0; denoise_position < frame_length; denoise_position += rnnoise_size) | ||||
| 			{ | ||||
| 				memset(subframe_buffer, 0, rnnoise_size * sizeof(float)); | ||||
| 				memcpy(subframe_buffer, g_local_voice_buffer + denoise_position, min(rnnoise_size * sizeof(float), (SRB2_VOICE_OPUS_FRAME_SIZE - denoise_position) * sizeof(float))); | ||||
| 				memcpy(subframe_buffer, frame_buffer + denoise_position, min(rnnoise_size * sizeof(float), (frame_length - denoise_position) * sizeof(float))); | ||||
| 				renamenoise_process_frame(g_local_renamenoise_state, denoise_buffer, subframe_buffer); | ||||
| 				memcpy(g_local_voice_buffer + denoise_position, denoise_buffer, min(rnnoise_size * sizeof(float), (SRB2_VOICE_OPUS_FRAME_SIZE - denoise_position) * sizeof(float))); | ||||
| 				memcpy(frame_buffer + denoise_position, denoise_buffer, min(rnnoise_size * sizeof(float), (frame_length - denoise_position) * sizeof(float))); | ||||
| 			} | ||||
| 			Z_Free(denoise_buffer); | ||||
| 			Z_Free(subframe_buffer); | ||||
| 		} | ||||
| 
 | ||||
| 		float softmem = 0.f; | ||||
| 		opus_pcm_soft_clip(g_local_voice_buffer, SRB2_VOICE_OPUS_FRAME_SIZE, 1, &softmem); | ||||
| 		opus_pcm_soft_clip(frame_buffer, frame_length, 1, &softmem); | ||||
| 
 | ||||
| 		// Voice detection gate open/close
 | ||||
| 		float maxamplitude = 0.f; | ||||
| 		for (int i = 0; i < g_local_voice_buffer_len; i++) | ||||
| 		for (int i = 0; i < frame_length; i++) | ||||
| 		{ | ||||
| 			maxamplitude = max(fabsf(g_local_voice_buffer[i]), maxamplitude); | ||||
| 			maxamplitude = max(fabsf(frame_buffer[i]), maxamplitude); | ||||
| 		} | ||||
| 		// 20. * log_10(amplitude) -> decibels (up to 0)
 | ||||
| 		// lower than -30 dB is usually inaudible
 | ||||
|  | @ -7660,7 +7688,6 @@ void NetVoiceUpdate(void) | |||
| 		case 0: | ||||
| 			if (I_GetTime() - g_local_voice_threshold_time > 15) | ||||
| 			{ | ||||
| 				g_local_voice_buffer_len = 0; | ||||
| 				g_local_voice_detected = false; | ||||
| 				continue; | ||||
| 			} | ||||
|  | @ -7668,20 +7695,17 @@ void NetVoiceUpdate(void) | |||
| 		case 1: | ||||
| 			if (!g_voicepushtotalk_on) | ||||
| 			{ | ||||
| 				g_local_voice_buffer_len = 0; | ||||
| 				g_local_voice_detected = false; | ||||
| 				continue; | ||||
| 			} | ||||
| 			g_local_voice_detected = true; | ||||
| 			break; | ||||
| 		default: | ||||
| 			g_local_voice_buffer_len = 0; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (cv_voice_selfdeafen.value == 1) | ||||
| 		if (cv_voice_selfdeafen.value == 1 || g_voice_disabled) | ||||
| 		{ | ||||
| 			g_local_voice_buffer_len = 0; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -7696,7 +7720,7 @@ void NetVoiceUpdate(void) | |||
| 		} | ||||
| 		OpusEncoder *encoder = g_local_opus_encoder; | ||||
| 
 | ||||
| 		INT32 result = opus_encode_float(encoder, g_local_voice_buffer, SRB2_VOICE_OPUS_FRAME_SIZE, encoded, 1400); | ||||
| 		INT32 result = opus_encode_float(encoder, frame_buffer, frame_length, encoded, 1400); | ||||
| 		if (result < 0) | ||||
| 		{ | ||||
| 			continue; | ||||
|  | @ -7718,15 +7742,24 @@ void NetVoiceUpdate(void) | |||
| 			{ | ||||
| 				RecreatePlayerOpusDecoder(consoleplayer); | ||||
| 			} | ||||
| 			result = opus_decode_float(g_player_opus_decoders[consoleplayer], encoded, result, g_local_voice_buffer, SRB2_VOICE_OPUS_FRAME_SIZE, 0); | ||||
| 			S_QueueVoiceFrameFromPlayer(consoleplayer, g_local_voice_buffer, result * sizeof(float), false); | ||||
| 			result = opus_decode_float(g_player_opus_decoders[consoleplayer], encoded, result, frame_buffer, frame_length, 0); | ||||
| 			S_QueueVoiceFrameFromPlayer(consoleplayer, frame_buffer, result * sizeof(float), false); | ||||
| 		} | ||||
| 
 | ||||
| 		g_local_voice_buffer_len = 0; | ||||
| 		g_local_opus_frame += 1; | ||||
| 	} while (bytes_dequed > 0); | ||||
| 	} | ||||
| 
 | ||||
| 	if (buffer_offset > 0) | ||||
| 	{ | ||||
| 		memmove(g_local_voice_buffer, g_local_voice_buffer + buffer_offset, (g_local_voice_buffer_len - buffer_offset) * sizeof(float)); | ||||
| 		g_local_voice_buffer_len -= buffer_offset; | ||||
| 	} | ||||
| 
 | ||||
| 	M_ExitFloatDenormalToZero(dnzstate); | ||||
| 
 | ||||
| 	if (denoise_buffer) Z_Free(denoise_buffer); | ||||
| 	if (subframe_buffer) Z_Free(subframe_buffer); | ||||
| 	if (encoded) Z_Free(encoded); | ||||
| 	ps_voiceupdatetime = I_GetPreciseTime() - ps_voiceupdatetime; | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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						"70ad11d77048078ee0adb7891068bf38" | ||||
| #define ASSET_HASH_SCRIPTS_PK3					"c3440a9ee57b4d9a81145a09afa0c0d6" | ||||
| #define ASSET_HASH_GFX_PK3						"314e88e73c0a629df9c2851dd7c21baf" | ||||
| #define ASSET_HASH_TEXTURES_GENERAL_PK3			"609b683d3efc291ea28dd4e50d731f34" | ||||
| #define ASSET_HASH_TEXTURES_SEGAZONES_PK3		"61a19cb324e66b84e0fbc07abb659c64" | ||||
| #define ASSET_HASH_TEXTURES_ORIGINALZONES_PK3	"2f3aa120be2dfb1f4fe3e7090fbb0948" | ||||
| #define ASSET_HASH_BIOS_PK3						"36201c4690256d133dff7d3879436dff" | ||||
| #define ASSET_HASH_SCRIPTS_PK3					"56be3c47192870c3265f19cf024e860e" | ||||
| #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						"84503a914248842b3e88db4f1080b8e6" | ||||
| #define ASSET_HASH_MAPS_PK3						"d744ac9747e078220a986ab295721182" | ||||
| #define ASSET_HASH_UNLOCKS_PK3					"a4de35ba9f83829ced44dfc1316ba33e" | ||||
| #define ASSET_HASH_STAFFGHOSTS_PK3				"f2ea75218c9b8ef479a75f287cc32d11" | ||||
| #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); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -843,6 +843,8 @@ static bool D_Display(bool world) | |||
| 
 | ||||
| tic_t rendergametic; | ||||
| 
 | ||||
| extern "C" consvar_t cv_skiprender; | ||||
| 
 | ||||
| void D_SRB2Loop(void) | ||||
| { | ||||
| 	tic_t entertic = 0, oldentertics = 0, realtics = 0, rendertimeout = INFTICS; | ||||
|  | @ -862,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.
 | ||||
| 
 | ||||
|  | @ -1123,6 +1126,13 @@ void D_SRB2Loop(void) | |||
| 			else | ||||
| 				frameskip = 0; | ||||
| 		} | ||||
| 		else if (cv_skiprender.value > 1) | ||||
| 		{ | ||||
| 			if (frameskip < cv_skiprender.value) | ||||
| 				frameskip++; | ||||
| 			else | ||||
| 				frameskip = 0; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			if (!ranwipe && frameskip < 3 && deltatics > 1.0) | ||||
|  | @ -1588,6 +1598,7 @@ void D_SRB2Main(void) | |||
| 
 | ||||
| 	// for dedicated server
 | ||||
| 	dedicated = M_CheckParm("-dedicated") != 0; | ||||
| 	connectedtodedicated = dedicated; | ||||
| 	if (dedicated) | ||||
| 	{ | ||||
| 		usedTourney = true; | ||||
|  | @ -2405,5 +2416,6 @@ void D_TakeMapSnapshots(void) | |||
| 		setmodeneeded = old_mode + 1; | ||||
| 		D_Display(true); | ||||
| 	} | ||||
| 	g_takemapthumbnail = TMT_NO; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										121
									
								
								src/d_netcmd.c
									
										
									
									
									
								
							
							
						
						
									
										121
									
								
								src/d_netcmd.c
									
										
									
									
									
								
							|  | @ -618,7 +618,23 @@ static boolean AllowedPlayerNameChar(char ch) | |||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) | ||||
| boolean IsPlayerNameUnique(const char *name, INT32 playernum) | ||||
| { | ||||
| 	// Check if a player is currently using the name, case-insensitively.
 | ||||
| 	for (INT32 ix = 0; ix < MAXPLAYERS; ix++) | ||||
| 	{ | ||||
| 		if (ix == playernum) // Don't compare with themself.
 | ||||
| 			continue; | ||||
| 		if (playeringame[ix] == false) // This player is not ingame.
 | ||||
| 			continue; | ||||
| 		if (strcasecmp(name, player_names[ix]) == 0) // Are usernames equal?
 | ||||
| 			return false; | ||||
| 	} | ||||
| 	 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| boolean IsPlayerNameGood(char *name) | ||||
| { | ||||
| 	size_t ix, len = strlen(name); | ||||
| 
 | ||||
|  | @ -650,36 +666,43 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) | |||
| 	for (ix = 0; ix < len; ix++) | ||||
| 		if (!AllowedPlayerNameChar(name[ix])) | ||||
| 			return false; | ||||
| 		 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| 	// Check if a player is currently using the name, case-insensitively.
 | ||||
| 	for (ix = 0; ix < MAXPLAYERS; ix++) | ||||
| boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) | ||||
| { | ||||
| 	size_t len = strlen(name); | ||||
| 	 | ||||
| 	// Check if a player is using a valid name.
 | ||||
| 	if (!IsPlayerNameGood(name)) | ||||
| 		return false; | ||||
| 
 | ||||
| 	// Check if another player is currently using the name, case-insensitively.
 | ||||
| 	if (!IsPlayerNameUnique(name, playernum))  | ||||
| 	{ | ||||
| 		if (ix != (size_t)playernum && playeringame[ix] | ||||
| 			&& strcasecmp(name, player_names[ix]) == 0) | ||||
| 		{ | ||||
| 			// We shouldn't kick people out just because
 | ||||
| 			// they joined the game with the same name
 | ||||
| 			// as someone else -- modify the name instead.
 | ||||
| 		// We shouldn't kick people out just because
 | ||||
| 		// they joined the game with the same name
 | ||||
| 		// as someone else -- modify the name instead.
 | ||||
| 
 | ||||
| 			// Recursion!
 | ||||
| 			// Slowly strip characters off the end of the
 | ||||
| 			// name until we no longer have a duplicate.
 | ||||
| 			if (len > 1) | ||||
| 			{ | ||||
| 				name[len-1] = '\0'; | ||||
| 				if (!EnsurePlayerNameIsGood (name, playernum)) | ||||
| 					return false; | ||||
| 			} | ||||
| 			else if (len == 1) // Agh!
 | ||||
| 			{ | ||||
| 				// Last ditch effort...
 | ||||
| 				sprintf(name, "%d", 'A' + M_RandomKey(26)); | ||||
| 				if (!EnsurePlayerNameIsGood (name, playernum)) | ||||
| 					return false; | ||||
| 			} | ||||
| 			else | ||||
| 		// Recursion!
 | ||||
| 		// Slowly strip characters off the end of the
 | ||||
| 		// name until we no longer have a duplicate.
 | ||||
| 		if (len > 1) | ||||
| 		{ | ||||
| 			name[len-1] = '\0'; | ||||
| 			if (!EnsurePlayerNameIsGood (name, playernum)) | ||||
| 				return false; | ||||
| 		} | ||||
| 		else if (len == 1) // Agh!
 | ||||
| 		{ | ||||
| 			// Last ditch effort...
 | ||||
| 			sprintf(name, "%d", 'A' + M_RandomKey(26)); | ||||
| 			if (!EnsurePlayerNameIsGood (name, playernum)) | ||||
| 				return false; | ||||
| 		} | ||||
| 		else | ||||
| 			return false; | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
|  | @ -2992,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; | ||||
| 
 | ||||
|  | @ -4451,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; | ||||
|  | @ -4639,10 +4672,10 @@ static void Command_ListWADS_f(void) | |||
| 		nameonly(tempname = va("%s", wadfiles[i]->filename)); | ||||
| 		if (!i) | ||||
| 			CONS_Printf("\x82 IWAD\x80: %s\n", tempname); | ||||
| 		else if (i <= mainwads) | ||||
| 		else if (i < mainwads) | ||||
| 			CONS_Printf("\x82 * %.2d\x80: %s\n", i, tempname); | ||||
| 		else if (!wadfiles[i]->important) | ||||
| 			CONS_Printf("\x86 %c %.2d: %s\n", ((i <= mainwads + musicwads) ? '*' : ' '), i, tempname); | ||||
| 			CONS_Printf("\x86 %c %.2d: %s\n", ((i < mainwads + musicwads) ? '*' : ' '), i, tempname); | ||||
| 		else | ||||
| 			CONS_Printf("   %.2d: %s\n", i, tempname); | ||||
| 	} | ||||
|  | @ -5696,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(); | ||||
| } | ||||
|  | @ -6067,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) | ||||
| 			{ | ||||
|  | @ -6876,6 +6914,7 @@ static void Command_Staffsync(void) | |||
| 		demostarttime = I_GetTime(); | ||||
| 
 | ||||
| 		staffbrief = mapheader->ghostBrief[staffsync_ghost]; | ||||
| 
 | ||||
| 		G_DoPlayDemoEx("", (staffbrief->wad << 16) | staffbrief->lump); | ||||
| 
 | ||||
| 		staffsync_ghost++; | ||||
|  | @ -6923,6 +6962,28 @@ static void Command_Staffsync(void) | |||
| 
 | ||||
| 			CONS_Printf("\n"); | ||||
| 
 | ||||
| 			CONS_Printf("   %d syncs (%d error)\n", result->numerror, result->totalerror/FRACUNIT); | ||||
| 
 | ||||
| 			CONS_Printf("   presync: "); | ||||
| 
 | ||||
| 			for (UINT32 j = 0; j < PRNUMSYNCED; j++) | ||||
| 			{ | ||||
| 				if (result->rngerror_presync[j] > 0) | ||||
| 					CONS_Printf("%s %d  ", rng_class_names[j], result->rngerror_presync[j]); | ||||
| 			} | ||||
| 
 | ||||
| 			CONS_Printf("\n"); | ||||
| 
 | ||||
| 			CONS_Printf("   postsync: "); | ||||
| 
 | ||||
| 			for (UINT32 j = 0; j < PRNUMSYNCED; j++) | ||||
| 			{ | ||||
| 				if (result->rngerror_postsync[j] > 0) | ||||
| 					CONS_Printf("%s %d  ", rng_class_names[j], result->rngerror_postsync[j]); | ||||
| 			} | ||||
| 
 | ||||
| 			CONS_Printf("\n"); | ||||
| 
 | ||||
| 			i++; | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -200,6 +200,8 @@ extern const char *netxcmdnames[MAXNETXCMD - 1]; | |||
| void D_RegisterServerCommands(void); | ||||
| void D_RegisterClientCommands(void); | ||||
| void CleanupPlayerName(INT32 playernum, const char *newname); | ||||
| boolean IsPlayerNameUnique(const char *name, INT32 playernum); | ||||
| boolean IsPlayerNameGood(char *name); | ||||
| boolean EnsurePlayerNameIsGood(char *name, INT32 playernum); | ||||
| void D_FillPlayerSkinAndColor(const UINT8 n, const player_t *player, player_config_t *config); | ||||
| void D_PlayerChangeSkinAndColor(player_t *player, UINT16 skin, UINT16 color, INT16 follower, UINT16 followercolor); | ||||
|  |  | |||
|  | @ -163,10 +163,10 @@ UINT8 *PutFileNeeded(UINT16 firstfile) | |||
| #ifdef DEVELOP | ||||
| 	i = 0; | ||||
| #else | ||||
| 	i = mainwads + 1; | ||||
| 	i = mainwads + musicwads; | ||||
| #endif | ||||
| 
 | ||||
| 	for (; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad
 | ||||
| 	for (; i < numwadfiles; i++) //mainwads+musicwads, otherwise we start on the first mainwad
 | ||||
| 	{ | ||||
| 		// If it has only music/sound lumps, don't put it in the list
 | ||||
| 		if (!wadfiles[i]->important) | ||||
|  | @ -197,7 +197,7 @@ UINT8 *PutFileNeeded(UINT16 firstfile) | |||
| 
 | ||||
| 		/* don't send mainwads!! */ | ||||
| #ifdef DEVELOP | ||||
| 		if (i <= mainwads) | ||||
| 		if (i < mainwads) | ||||
| 			filestatus += (2 << 4); | ||||
| #endif | ||||
| 
 | ||||
|  | @ -565,7 +565,7 @@ INT32 CL_CheckFiles(void) | |||
| #ifdef DEVELOP | ||||
| 		j = 0; | ||||
| #else | ||||
| 		j = mainwads + 1; | ||||
| 		j = mainwads + musicwads; | ||||
| #endif | ||||
| 		for (i = 0; i < fileneedednum || j < numwadfiles;) | ||||
| 		{ | ||||
|  |  | |||
|  | @ -154,6 +154,8 @@ typedef enum | |||
| 	PF2_SERVERTEMPMUTE		= 1<<10, // Haven't met gamestochat requirement
 | ||||
| 	PF2_SAMEFRAMESTUNG		= 1<<11, // Goofy bullshit for tracking mutual ring sting
 | ||||
| 	PF2_UNSTINGABLE			= 1<<12, // Was bumped out of spindash
 | ||||
| 	PF2_GIMMESTARTAWARDS	= 1<<13, // Need to apply non-first start awards on a 1 tic delay to prevent port priority
 | ||||
| 	PF2_GIMMEFIRSTBLOOD		= 1<<14, // And need to differentiate between First Blood and everything else!
 | ||||
| } pflags2_t; | ||||
| 
 | ||||
| typedef enum | ||||
|  | @ -383,7 +385,7 @@ typedef enum | |||
| #define TUMBLEBOUNCES 3 | ||||
| #define TUMBLEGRAVITY (4*FRACUNIT) | ||||
| 
 | ||||
| #define TRIPWIRETIME (15) | ||||
| #define TRIPWIRETIME (50) | ||||
| 
 | ||||
| #define BALLHOGINCREMENT (7) | ||||
| 
 | ||||
|  | @ -736,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.
 | ||||
|  | @ -755,6 +758,7 @@ struct player_t | |||
| 	tic_t lastairtime; | ||||
| 	UINT16 bigwaypointgap;	// timer counts down if finish line distance gap is too big to update waypoint
 | ||||
| 	UINT8 startboost;		// (0 to 125) - Boost you get from start of race
 | ||||
| 	UINT8 neostartboost;	// Weaker partial startboost
 | ||||
| 	UINT8 dropdashboost;	// Boost you get when holding A while respawning
 | ||||
| 	UINT8 aciddropdashboost;	// acid dropdash
 | ||||
| 
 | ||||
|  | @ -766,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
 | ||||
|  | @ -801,6 +806,8 @@ struct player_t | |||
| 	fixed_t spindashspeed;	// Spindash release speed
 | ||||
| 	UINT8 spindashboost;	// Spindash release boost timer
 | ||||
| 
 | ||||
| 	UINT8 ringboostinprogress; // Ring overhead, don't sting!
 | ||||
| 
 | ||||
| 	fixed_t fastfall;		// Fast fall momentum
 | ||||
| 	fixed_t fastfallBase;	// Fast fall base speed multiplier
 | ||||
| 
 | ||||
|  | @ -821,6 +828,7 @@ struct player_t | |||
| 	UINT16 tripwireLeniency;	// When reaching a state that lets you go thru tripwire, you get an extra second leniency after it ends to still go through it.
 | ||||
| 	UINT8 tripwireAirLeniency;	// Timer that elongates tripwire leniency when in midair.
 | ||||
| 	UINT8 fakeBoost;	// Some items need to grant tripwire pass briefly, even when their effect is thrust/instathrust. This is a fake boost type to control that.
 | ||||
| 	UINT16 subsonicleniency; // Keep the subsonic visual for just a little bit when your sonic boom is visible
 | ||||
| 
 | ||||
| 	itemroulette_t itemRoulette;	// Item roulette data
 | ||||
| 
 | ||||
|  | @ -840,6 +848,7 @@ struct player_t | |||
| 	UINT8 pickuprings;	// Number of rings being picked up before added to the counter (prevents rings from being deleted forever over 20)
 | ||||
| 	UINT8 ringdelay;	// (0 to 3) - 3 tic delay between every ring usage
 | ||||
| 	UINT16 ringboost;	// Ring boost timer
 | ||||
| 	UINT16 momentboost; // Sigh
 | ||||
| 	UINT8 sparkleanim;	// (0 to 19) - Angle offset for ring sparkle animation
 | ||||
| 	UINT16 superring;	// You were awarded rings, and have this many of them left to spawn on yourself.
 | ||||
| 	UINT16 superringdisplay; // For HUD countup when awarded superring
 | ||||
|  | @ -1071,8 +1080,6 @@ struct player_t | |||
| 	UINT16 wavedashboost; // The actual boost granted from wavedash.
 | ||||
| 	fixed_t wavedashpower; // Is this a bullshit "tap" wavedash? Weaken lower-charge wavedashes while keeping long sliptides fully rewarding.
 | ||||
| 
 | ||||
| 	UINT16 speedpunt; | ||||
| 
 | ||||
| 	UINT16 trickcharge; // Landed normally from a trick panel? Get the benefits package!
 | ||||
| 
 | ||||
| 	UINT16 infinitether; // Generic infinitether time, used for infinitether leniency.
 | ||||
|  | @ -1129,6 +1136,8 @@ struct player_t | |||
| 	boolean stingfx; | ||||
| 	UINT8 bumperinflate; | ||||
| 
 | ||||
| 	boolean mfdfinish; // Did you cross the finish line while just about to explode?
 | ||||
| 
 | ||||
| 	UINT8 ringboxdelay; // Delay until Ring Box auto-activates
 | ||||
| 	UINT8 ringboxaward; // Where did we stop?
 | ||||
| 	UINT32 lastringboost; // What was our accumulated boost when locking the award?
 | ||||
|  |  | |||
|  | @ -314,6 +314,16 @@ static inline int lib_getenum(lua_State *L) | |||
| 		if (mathlib) return luaL_error(L, "playerflag '%s' could not be found.\n", word); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	else if (fastncmp("PF2_", word, 4)) { | ||||
| 		p = word+4; | ||||
| 		for (i = 0; PLAYERFLAG2_LIST[i]; i++) | ||||
| 			if (fastcmp(p, PLAYERFLAG2_LIST[i])) { | ||||
| 				lua_pushinteger(L, ((lua_Integer)1<<i)); | ||||
| 				return 1; | ||||
| 			} | ||||
| 		if (mathlib) return luaL_error(L, "playerflag2 '%s' could not be found.\n", word); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	else if (fastncmp("GT_", word, 3)) { | ||||
| 		p = word; | ||||
| 		i = 0; | ||||
|  | @ -382,6 +392,11 @@ static inline int lib_getenum(lua_State *L) | |||
| 			lua_pushinteger(L, (lua_Integer)ML_WRAPMIDTEX); | ||||
| 			return 1; | ||||
| 		} | ||||
| 		if (fastcmp(p, "EFFECT6")) | ||||
| 		{ | ||||
| 			lua_pushinteger(L, (lua_Integer)ML_MIDTEXINVISWALL); | ||||
| 			return 1; | ||||
| 		} | ||||
| 		if (mathlib) return luaL_error(L, "linedef flag '%s' could not be found.\n", word); | ||||
| 		return 0; | ||||
| 	} | ||||
|  |  | |||
|  | @ -1599,6 +1599,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi | |||
| 	"S_AMPBURST", | ||||
| 
 | ||||
| 	// Tripwire VFX on player for bumping it or passing it
 | ||||
| 	 | ||||
| 	"S_SONICBOOM", | ||||
| 	"S_TRIPWIREOK", | ||||
| 	"S_TRIPWIRELOCKOUT", | ||||
| 
 | ||||
|  | @ -3597,6 +3599,7 @@ const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for sanity t | |||
| 	"MT_AMPAURA", | ||||
| 	"MT_AMPBURST", | ||||
| 
 | ||||
| 	"MT_SONICBOOM", | ||||
| 	"MT_TRIPWIREOK", | ||||
| 	"MT_TRIPWIRELOCKOUT", | ||||
| 
 | ||||
|  | @ -4212,6 +4215,25 @@ const char *const PLAYERFLAG_LIST[] = { | |||
| 	NULL // stop loop here.
 | ||||
| }; | ||||
| 
 | ||||
| const char *const PLAYERFLAG2_LIST[] = { | ||||
| 	"SELFMUTE", | ||||
| 	"SELFDEAFEN", | ||||
| 	"SERVERMUTE", | ||||
| 	"SERVERDEAFEN", | ||||
| 	 | ||||
| 	"STRICTFASTFALL", // Fastfall only with C, never with A+X. Profile preference.
 | ||||
| 	 | ||||
| 	"ALWAYSDAMAGED", // Ignore invulnerability or clash conditions when evaulating damage (P_DamageMobj). Unset after use!
 | ||||
| 	"BUBBLECONTACT", // ACHTUNG VERY BAD HACK - Don't allow Bubble Shield to contact certain objects unless this is a fresh blowup.
 | ||||
| 	"SUPERTRANSFERVFX", // Don't respawn the "super transfer available" VFX.
 | ||||
| 	"FASTTUMBLEBOUNCE", // Don't lose speed when tumblebouncing.
 | ||||
| 	 | ||||
| 	"SERVERTEMPMUTE", // Haven't met gamestochat requirement
 | ||||
| 	"SAMEFRAMESTUNG", // Goofy bullshit for tracking mutual ring sting
 | ||||
| 	"UNSTINGABLE", // Was bumped out of spindash
 | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| const char *const GAMETYPERULE_LIST[] = { | ||||
| 	"CIRCUIT", | ||||
| 	"BOTS", | ||||
|  |  | |||
|  | @ -65,6 +65,7 @@ extern const char *const MOBJFLAG2_LIST[]; // \tMF2_(\S+).*// (.+) --> \t"\1", / | |||
| extern const char *const MOBJEFLAG_LIST[]; | ||||
| extern const char *const MAPTHINGFLAG_LIST[4]; | ||||
| extern const char *const PLAYERFLAG_LIST[]; | ||||
| extern const char *const PLAYERFLAG2_LIST[]; | ||||
| extern const char *const GAMETYPERULE_LIST[]; | ||||
| extern const char *const ML_LIST[]; // Linedef flags
 | ||||
| extern const char *const MSF_LIST[]; // Sector flags
 | ||||
|  |  | |||
|  | @ -159,6 +159,9 @@ enum | |||
| 
 | ||||
| 	// Transfers FOF properties.
 | ||||
| 	ML_TFERLINE			= 0x00008000, | ||||
| 
 | ||||
| 	// Like ML_WRAPMIDTEX, but invisible wall style instead
 | ||||
| 	ML_MIDTEXINVISWALL	= 0x00010000, | ||||
| }; | ||||
| 
 | ||||
| enum | ||||
|  |  | |||
|  | @ -457,7 +457,7 @@ enum { | |||
| 
 | ||||
| 
 | ||||
| */ | ||||
| void I_Error(const char *error, ...) FUNCIERROR; | ||||
| FUNCIERROR void ATTRNORETURN I_Error(const char *error, ...); | ||||
| 
 | ||||
| /**	\brief	write a message to stderr (use before I_Quit) for when you need to quit with a msg, but need
 | ||||
|  the return code 0 of I_Quit(); | ||||
|  | @ -757,8 +757,8 @@ extern int | |||
| #define EXP_STABLERATE 3*FRACUNIT/10 // how low is your placement before losing XP? 4*FRACUNIT/10 = top 40% of race will gain
 | ||||
| #define EXP_POWER 3*FRACUNIT/100 // adjust to change overall xp volatility
 | ||||
| #define EXP_MIN 25 // The min value target
 | ||||
| #define EXP_TARGET 120 // Used for grading ...
 | ||||
| #define EXP_MAX 120 // The max value displayed by the hud and in the tally screen and GP results screen
 | ||||
| #define EXP_TARGET 150 // Used for grading ...
 | ||||
| #define EXP_MAX 150 // The max value displayed by the hud and in the tally screen and GP results screen
 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } // extern "C"
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  | @ -245,6 +246,10 @@ struct staffsync_t | |||
| 	char name[MAXPLAYERNAME+1]; | ||||
| 	UINT32 reason; | ||||
| 	UINT32 extra; | ||||
| 	fixed_t totalerror; | ||||
| 	UINT32 numerror; | ||||
| 	UINT32 rngerror_presync[32]; | ||||
| 	UINT32 rngerror_postsync[32]; | ||||
| }; | ||||
| extern staffsync_t staffsync_results[1024]; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -1593,6 +1593,7 @@ void F_StartGameEnd(void) | |||
| 	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); | ||||
| 	F_WipeEndScreen(); | ||||
| 	F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); | ||||
| 	Music_Stop("credits"); | ||||
| 
 | ||||
| 	nextmap = NEXTMAP_TITLE; | ||||
| 	G_EndGame(); | ||||
|  |  | |||
|  | @ -182,9 +182,10 @@ demoghost *ghosts = NULL; | |||
| // - 0x000F -- RR 2.4 indev (staff ghosts part 2 - dynslopes thinker fix)
 | ||||
| // - 0x0010 -- RR 2.4 rc1   (staff ghosts part 3 - skinlimit raise. don't say we never did anythin for 'ya)
 | ||||
| // - 0x0011 -- RR 2.4 rc2   (K_FlipFromObject oversight)
 | ||||
| // - 0x0012 -- RR 2.4 rc6	(Waterskii regression from 2.3)
 | ||||
| 
 | ||||
| #define MINDEMOVERSION 0x000E | ||||
| #define DEMOVERSION 0x0011 | ||||
| #define DEMOVERSION 0x0012 | ||||
| 
 | ||||
| boolean G_CompatLevel(UINT16 level) | ||||
| { | ||||
|  | @ -303,18 +304,39 @@ boolean G_ConsiderEndingDemoRead(void) | |||
| } | ||||
| 
 | ||||
| // Demo failed sync during a sync test! Log the failure to be reported later.
 | ||||
| static void G_FailStaffSync(staffsync_reason_t reason, UINT32 extra) | ||||
| static boolean G_FailStaffSync(staffsync_reason_t reason, UINT32 extra) | ||||
| { | ||||
| 	if (demo.attract != DEMO_ATTRACT_OFF) // Don't shout about RNG desyncs in titledemos
 | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (!staffsync) | ||||
| 		return; | ||||
| 		return true; | ||||
| 
 | ||||
| 	if (staffsync_results[staffsync_failed].reason != 0) | ||||
| 		return; | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (reason == SYNC_RNG) | ||||
| 	{ | ||||
| 		switch (extra) | ||||
| 		{ | ||||
| 			case PR_ITEM_DEBRIS: | ||||
| 			case PR_RANDOMAUDIENCE: | ||||
| 			case PR_VOICES: | ||||
| 			case PR_DECORATION: | ||||
| 			case PR_RANDOMANIM: | ||||
| 				CONS_Printf("[!] Ignored desync from RNG class %d\n", extra); | ||||
| 				return false; | ||||
| 			default: | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	staffsync_results[staffsync_failed].map = gamemap; | ||||
| 	memcpy(&staffsync_results[staffsync_failed].name, player_names[consoleplayer], sizeof(player_names[consoleplayer])); | ||||
| 	staffsync_results[staffsync_failed].reason = reason; | ||||
| 	staffsync_results[staffsync_failed].extra = extra; | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void G_ReadDemoExtraData(void) | ||||
|  | @ -484,13 +506,26 @@ void G_ReadDemoExtraData(void) | |||
| 					{ | ||||
| 						P_SetRandSeed(static_cast<pr_class_t>(i), rng); | ||||
| 
 | ||||
| 						if (demosynced) | ||||
| 						if (staffsync) | ||||
| 						{ | ||||
| 							CONS_Alert(CONS_WARNING, "Demo playback has desynced (RNG class %d)!\n", i); | ||||
| 							G_FailStaffSync(SYNC_RNG, i); | ||||
| 							if (demosynced) | ||||
| 								staffsync_results[staffsync_failed].rngerror_presync[i]++; | ||||
| 							else | ||||
| 								staffsync_results[staffsync_failed].rngerror_postsync[i]++; | ||||
| 						} | ||||
| 
 | ||||
| 						storesynced = false; | ||||
| 						if (demosynced) | ||||
| 						{ | ||||
| 							if (G_FailStaffSync(SYNC_RNG, i)) | ||||
| 							{ | ||||
| 								CONS_Alert(CONS_WARNING, "Demo playback has desynced (RNG class %d - %s)!\n", i, rng_class_names[i]); | ||||
| 								storesynced = false; | ||||
| 							} | ||||
| 						} | ||||
| 						else | ||||
| 						{ | ||||
| 							storesynced = false; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				demosynced = storesynced; | ||||
|  | @ -1242,6 +1277,12 @@ void G_ConsGhostTic(INT32 playernum) | |||
| 				} | ||||
| 				demosynced = false; | ||||
| 
 | ||||
| 				if (staffsync) | ||||
| 				{ | ||||
| 					staffsync_results[staffsync_failed].numerror++; | ||||
| 					staffsync_results[staffsync_failed].totalerror += abs(testmo->x - oldghost[playernum].x) + abs(testmo->y - oldghost[playernum].y) + abs(testmo->z - oldghost[playernum].z); | ||||
| 				} | ||||
| 
 | ||||
| 				P_UnsetThingPosition(testmo); | ||||
| 				testmo->x = oldghost[playernum].x; | ||||
| 				testmo->y = oldghost[playernum].y; | ||||
|  | @ -3269,6 +3310,8 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) | |||
| 
 | ||||
| 	// net var data
 | ||||
| 	demobuf.p += CV_LoadDemoVars(demobuf.p); | ||||
| 	// Dumb hack for team play desyncs - https://gitlab.com/kart-krew-dev/ring-racers/-/issues/210
 | ||||
| 	g_teamplay = cv_teamplay.value ? 1 : 0; | ||||
| 
 | ||||
| 	memset(&grandprixinfo, 0, sizeof grandprixinfo); | ||||
| 	if ((demoflags & DF_GRANDPRIX)) | ||||
|  | @ -3474,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; | ||||
| 
 | ||||
|  | @ -3504,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; | ||||
|  | @ -4169,6 +4216,7 @@ boolean G_CheckDemoStatus(void) | |||
| 
 | ||||
| 	if (modeattacking || demo.willsave) | ||||
| 	{ | ||||
| 		demo.willsave = false; | ||||
| 		if (demobuf.p) | ||||
| 		{ | ||||
| 			G_SaveDemo(); | ||||
|  |  | |||
							
								
								
									
										136
									
								
								src/g_game.c
									
										
									
									
									
								
							
							
						
						
									
										136
									
								
								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: | ||||
|  | @ -1885,7 +1892,7 @@ void G_Ticker(boolean run) | |||
| 
 | ||||
| 	P_MapStart(); | ||||
| 
 | ||||
| 	if (demo.playback && staffsync && !demosynced) | ||||
| 	if (demo.playback && staffsync && !demosynced && false) // We want to assess the magnitude of position desync, don't bail early!
 | ||||
| 	{ | ||||
| 		G_ClearRetryFlag(); | ||||
| 		G_StopDemo(); | ||||
|  | @ -2312,6 +2319,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) | |||
| 	UINT16 bigwaypointgap; | ||||
| 
 | ||||
| 	INT16 duelscore; | ||||
| 	 | ||||
| 	boolean mfdfinish; | ||||
| 
 | ||||
| 	roundconditions_t roundconditions; | ||||
| 	boolean saveroundconditions; | ||||
|  | @ -2406,6 +2415,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) | |||
| 
 | ||||
| 	totalring = players[player].totalring; | ||||
| 	xtralife = players[player].xtralife; | ||||
| 	 | ||||
| 	mfdfinish = players[player].mfdfinish; | ||||
| 
 | ||||
| 	pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE|PF_ANALOGSTICK|PF_AUTORING)); | ||||
| 	pflags2 = (players[player].pflags2 & (PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERTEMPMUTE | PF2_SERVERMUTE | PF2_SERVERDEAFEN | PF2_STRICTFASTFALL)); | ||||
|  | @ -2483,6 +2494,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) | |||
| 		lastsafecheatcheck = 0; | ||||
| 		bigwaypointgap = 0; | ||||
| 		duelscore = 0; | ||||
| 		mfdfinish = 0; | ||||
| 
 | ||||
| 		finalized = false; | ||||
| 
 | ||||
|  | @ -2675,10 +2687,12 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) | |||
| 	p->xtralife = xtralife; | ||||
| 
 | ||||
| 	p->finalized = finalized; | ||||
| 	 | ||||
| 	p->mfdfinish = mfdfinish; | ||||
| 
 | ||||
| 	// SRB2kart
 | ||||
| 	p->itemtype = itemtype; | ||||
| 	p->itemamount = itemamount; | ||||
| 	K_SetPlayerItemAmount(p, itemamount); | ||||
| 	p->growshrinktimer = growshrinktimer; | ||||
| 	p->karmadelay = 0; | ||||
| 	p->eggmanblame = -1; | ||||
|  | @ -4057,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--; | ||||
|  | @ -4071,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 | ||||
| } | ||||
| 
 | ||||
| //
 | ||||
|  | @ -4195,6 +4289,7 @@ void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencor | |||
| 	UINT8 i, levelindex = 0, bonusindex = 0; | ||||
| 	UINT8 bonusmodulo = max(1, (cup->numlevels+1)/(cup->numbonus+1)); | ||||
| 	UINT16 cupLevelNum; | ||||
| 	INT32 bonusgt; | ||||
| 
 | ||||
| 	// Levels are added to the queue in the following pattern.
 | ||||
| 	// For 5 Race rounds and 2 Bonus rounds, the most common case:
 | ||||
|  | @ -4236,9 +4331,17 @@ void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencor | |||
| 			if (cupLevelNum < nummapheaders) | ||||
| 			{ | ||||
| 				// In the case of Bonus rounds, we simply skip invalid maps.
 | ||||
| 				if ((mapheaderinfo[cupLevelNum]->typeoflevel & TOL_BATTLE) == TOL_BATTLE) | ||||
| 				{ | ||||
| 					bonusgt = GT_BATTLE; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					bonusgt = G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel); | ||||
| 				} | ||||
| 				G_MapIntoRoundQueue( | ||||
| 					cupLevelNum, | ||||
| 					G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel), | ||||
| 					bonusgt, | ||||
| 					setencore, // if this isn't correct, Got_Mapcmd will fix it
 | ||||
| 					false | ||||
| 				); | ||||
|  | @ -4306,9 +4409,22 @@ void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencor | |||
| 			cupLevelNum = emeraldcup->cachedlevels[CUPCACHE_SPECIAL]; | ||||
| 			if (cupLevelNum < nummapheaders) | ||||
| 			{ | ||||
| 				// In case of multiple TOLs, prioritize Special, then Versus, then guess.
 | ||||
| 				if ((mapheaderinfo[cupLevelNum]->typeoflevel & TOL_SPECIAL) == TOL_SPECIAL) | ||||
| 				{ | ||||
| 					bonusgt = GT_SPECIAL; | ||||
| 				} | ||||
| 				else if ((mapheaderinfo[cupLevelNum]->typeoflevel & TOL_VERSUS) == TOL_VERSUS) | ||||
| 				{ | ||||
| 					bonusgt = GT_VERSUS; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					bonusgt = G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel); | ||||
| 				} | ||||
| 				G_MapIntoRoundQueue( | ||||
| 					cupLevelNum, | ||||
| 					G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel), | ||||
| 					bonusgt, | ||||
| 					setencore, // if this isn't correct, Got_Mapcmd will fix it
 | ||||
| 					true // Rank-restricted!
 | ||||
| 				); | ||||
|  | @ -4968,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(); | ||||
| 
 | ||||
|  | @ -5106,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) | ||||
| 	{ | ||||
|  | @ -5910,7 +6032,7 @@ void G_SetRetryFlag(void) | |||
| { | ||||
| 	if (retrying == false && grandprixinfo.gp) | ||||
| 	{ | ||||
| 		if (!specialstageinfo.valid) | ||||
| 		if (grandprixinfo.eventmode != GPEVENT_SPECIAL) | ||||
| 			grandprixinfo.rank.continuesUsed++; | ||||
| 		grandprixinfo.rank.levels[grandprixinfo.rank.numLevels].continues++; | ||||
| 	} | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -92,7 +92,7 @@ void I_OsPolling(void); | |||
| 
 | ||||
| /**	\brief Called by M_Responder when quit is selected, return exit code 0
 | ||||
| */ | ||||
| void I_Quit(void) FUNCNORETURN; | ||||
| FUNCNORETURN void ATTRNORETURN I_Quit(void); | ||||
| 
 | ||||
| typedef enum | ||||
| { | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
							
								
								
									
										97
									
								
								src/info.c
									
										
									
									
									
								
							
							
						
						
									
										97
									
								
								src/info.c
									
										
									
									
									
								
							|  | @ -614,6 +614,7 @@ char sprnames[NUMSPRITES + 1][5] = | |||
| 
 | ||||
| 	"EXPC", | ||||
| 	 | ||||
| 	"TWBB", | ||||
| 	"TWOK", | ||||
| 	"TW_L", | ||||
| 
 | ||||
|  | @ -2225,6 +2226,7 @@ state_t states[NUMSTATES] = | |||
| 	{SPR_AMPD,                 FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 2, S_NULL}, // S_AMPAURA
 | ||||
| 	{SPR_AMPB,                 FF_FULLBRIGHT|FF_ADD|FF_PAPERSPRITE|2, -1, {NULL}, 4, 2, S_NULL}, // S_AMPBURST
 | ||||
| 
 | ||||
| 	{SPR_TWBB,                 FF_ADD|FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_SONICBOOM
 | ||||
| 	{SPR_TWOK,                 FF_FULLBRIGHT|FF_ANIMATE|0, 56, {NULL}, 55, 1, S_NULL}, // S_TRIPWIREOK
 | ||||
| 	{SPR_TW_L,                 FF_FULLBRIGHT|FF_ANIMATE|0, 56, {NULL}, 55, 1, S_NULL}, // S_TRIPWIRELOCKOUT
 | ||||
| 
 | ||||
|  | @ -13778,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -13805,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -13913,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -13940,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -13967,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -13994,7 +13996,34 @@ 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
 | ||||
| 	}, | ||||
| 
 | ||||
| 	{           // MT_SONICBOOM
 | ||||
| 		-1,             // doomednum
 | ||||
| 		S_SONICBOOM,    // spawnstate
 | ||||
| 		1000,           // spawnhealth
 | ||||
| 		S_NULL,         // seestate
 | ||||
| 		sfx_None,       // seesound
 | ||||
| 		0,              // reactiontime
 | ||||
| 		sfx_None,       // attacksound
 | ||||
| 		S_NULL,         // painstate
 | ||||
| 		0,              // painchance
 | ||||
| 		sfx_None,       // painsound
 | ||||
| 		S_NULL,         // meleestate
 | ||||
| 		S_NULL,         // missilestate
 | ||||
| 		S_NULL,         // deathstate
 | ||||
| 		S_NULL,         // xdeathstate
 | ||||
| 		sfx_None,       // deathsound
 | ||||
| 		0,              // speed
 | ||||
| 		67*FRACUNIT,    // radius
 | ||||
| 		67*FRACUNIT,    // height
 | ||||
| 		1,              // display offset
 | ||||
| 		100,            // mass
 | ||||
| 		0,              // damage
 | ||||
| 		sfx_None,       // activesound
 | ||||
| 		MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
 | ||||
| 		S_NULL          // raisestate
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14021,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14048,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14075,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14102,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14129,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14156,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14183,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14210,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -14237,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17532,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17559,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17586,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17613,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17640,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -17829,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -18991,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19019,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19047,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19452,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19479,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19614,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19668,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -19695,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -20100,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -20127,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
 | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -22735,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
 | ||||
|  | @ -22761,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
 | ||||
|  | @ -22787,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
 | ||||
|  |  | |||
|  | @ -1155,6 +1155,7 @@ typedef enum sprite | |||
| 
 | ||||
| 	SPR_EXPC, | ||||
| 	 | ||||
| 	SPR_TWBB, // Sonic Boom
 | ||||
| 	SPR_TWOK, // Tripwire OK
 | ||||
| 	SPR_TW_L, // Tripwire Lockout
 | ||||
| 
 | ||||
|  | @ -2681,6 +2682,7 @@ typedef enum state | |||
| 	S_AMPAURA, | ||||
| 	S_AMPBURST, | ||||
| 
 | ||||
| 	S_SONICBOOM, | ||||
| 	S_TRIPWIREOK, | ||||
| 	S_TRIPWIRELOCKOUT, | ||||
| 
 | ||||
|  | @ -4706,6 +4708,7 @@ typedef enum mobj_type | |||
| 	MT_AMPAURA, | ||||
| 	MT_AMPBURST, | ||||
| 
 | ||||
| 	MT_SONICBOOM, | ||||
| 	MT_TRIPWIREOK, | ||||
| 	MT_TRIPWIRELOCKOUT, | ||||
| 
 | ||||
|  |  | |||
|  | @ -133,8 +133,12 @@ void K_CheckBumpers(void) | |||
| { | ||||
| 	UINT8 i; | ||||
| 	UINT8 numingame = 0; | ||||
| 	UINT8 rednumingame = 0; | ||||
| 	UINT8 bluenumingame = 0; | ||||
| 	UINT8 nobumpers = 0; | ||||
| 	UINT8 eliminated = 0; | ||||
| 	UINT8 redeliminated = 0; | ||||
| 	UINT8 blueeliminated = 0; | ||||
| 	SINT8 kingofthehill = -1; | ||||
| 
 | ||||
| 	if (!(gametyperules & GTR_BUMPERS)) | ||||
|  | @ -143,6 +147,8 @@ void K_CheckBumpers(void) | |||
| 	if (gameaction == ga_completed) | ||||
| 		return; | ||||
| 
 | ||||
| 	boolean team = G_GametypeHasTeams(); | ||||
| 
 | ||||
| 	for (i = 0; i < MAXPLAYERS; i++) | ||||
| 	{ | ||||
| 		if (!playeringame[i] || players[i].spectator) // not even in-game
 | ||||
|  | @ -152,6 +158,11 @@ void K_CheckBumpers(void) | |||
| 			return; | ||||
| 
 | ||||
| 		numingame++; | ||||
| 		if (team) | ||||
| 		{ | ||||
| 			if (players[i].team == 1) rednumingame++; | ||||
| 			if (players[i].team == 2) bluenumingame++; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!P_MobjWasRemoved(players[i].mo) && players[i].mo->health <= 0) // if you don't have any bumpers, you're probably not a winner
 | ||||
| 		{ | ||||
|  | @ -161,6 +172,11 @@ void K_CheckBumpers(void) | |||
| 		if (players[i].pflags & PF_ELIMINATED) | ||||
| 		{ | ||||
| 			eliminated++; | ||||
| 			if (team) | ||||
| 			{ | ||||
| 				if (players[i].team == 1) redeliminated++; | ||||
| 				else if (players[i].team == 2) blueeliminated++; | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
|  | @ -168,6 +184,12 @@ void K_CheckBumpers(void) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	boolean teamwin = false; | ||||
| 	if (team && (rednumingame - redeliminated == 0 || bluenumingame - blueeliminated == 0)) | ||||
| 	{ | ||||
| 		teamwin = true; | ||||
| 	} | ||||
| 
 | ||||
| 	if (numingame - eliminated == 2 && battleovertime.enabled && battleovertime.radius <= BARRIER_MIN_RADIUS) | ||||
| 	{ | ||||
| 		Music_Stop("battle_overtime"); | ||||
|  | @ -186,9 +208,28 @@ void K_CheckBumpers(void) | |||
| 	{ | ||||
| 		// If every other player is eliminated, the
 | ||||
| 		// last player standing wins by default.
 | ||||
| 		if (eliminated >= numingame - 1) | ||||
| 		// Or, if an entire team is eliminated.
 | ||||
| 		if (eliminated >= numingame - 1 || teamwin) | ||||
| 		{ | ||||
| 			K_EndBattleRound(kingofthehill != -1 ? &players[kingofthehill] : NULL); | ||||
| 			if (teamwin) | ||||
| 			{ | ||||
| 				// Find the player with the highest individual score
 | ||||
| 				UINT32 highestscore = 0; | ||||
| 				UINT32 highestplayer = 0; | ||||
| 				for (i = 0; i < MAXPLAYERS; i++) | ||||
| 				{ | ||||
| 					if (playeringame[i] && players[i].score > highestscore) | ||||
| 					{ | ||||
| 						highestplayer = i; | ||||
| 						highestscore = players[i].score; | ||||
| 					} | ||||
| 				} | ||||
| 				K_EndBattleRound(&players[highestplayer]); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				K_EndBattleRound(kingofthehill != -1 ? &players[kingofthehill] : NULL); | ||||
| 			} | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
|  | @ -699,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; | ||||
|  | @ -708,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; | ||||
|  | @ -722,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; | ||||
|  | @ -988,6 +1038,8 @@ boolean K_EndBattleRound(player_t *victor) | |||
| 			// exiting, the round has already ended.
 | ||||
| 			return false; | ||||
| 		} | ||||
| 		 | ||||
| 		UINT32 topscore = 0; | ||||
| 
 | ||||
| 		if (gametyperules & GTR_POINTLIMIT) | ||||
| 		{ | ||||
|  | @ -996,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) | ||||
| 			{ | ||||
|  |  | |||
|  | @ -55,30 +55,16 @@ extern "C" consvar_t cv_forcebots; | |||
| --------------------------------------------------*/ | ||||
| void K_SetNameForBot(UINT8 newplayernum, const char *realname) | ||||
| { | ||||
| 	UINT8 ix = MAXPLAYERS; | ||||
| 
 | ||||
| 	// These names are generally sourced from skins.
 | ||||
| 	I_Assert(MAXPLAYERNAME >= SKINNAMESIZE+2); | ||||
| 
 | ||||
| 	boolean canApplyNameChange = true; | ||||
| 	if (netgame == true) | ||||
| 	{ | ||||
| 		// Check if a player is currently using the name, case-insensitively.
 | ||||
| 		// We only do this if online, because it doesn't matter if there are multiple Eggrobo *off*line.
 | ||||
| 		// See also EnsurePlayerNameIsGood
 | ||||
| 		for (ix = 0; ix < MAXPLAYERS; ix++) | ||||
| 		{ | ||||
| 			if (ix == newplayernum) | ||||
| 				continue; | ||||
| 			if (playeringame[ix] == false) | ||||
| 				continue; | ||||
| 			if (strcasecmp(realname, player_names[ix]) != 0) | ||||
| 				continue; | ||||
| 
 | ||||
| 			break; | ||||
| 		} | ||||
| 		canApplyNameChange = IsPlayerNameUnique(realname, newplayernum); | ||||
| 	} | ||||
| 
 | ||||
| 	if (ix == MAXPLAYERS) | ||||
| 	if (canApplyNameChange) | ||||
| 	{ | ||||
| 		// No conflict detected!
 | ||||
| 		sprintf(player_names[newplayernum], "%s", realname); | ||||
|  | @ -241,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; | ||||
|  | @ -357,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; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1034,10 +1039,6 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) | |||
| 			} | ||||
| 
 | ||||
| 			// if you're here, you're getting hit
 | ||||
| 			// Damage is a bit hacky, we want only a small loss-of-control
 | ||||
| 			// while still behaving as if it's a "real" hit.
 | ||||
| 			P_PlayRinglossSound(victim); | ||||
| 			P_PlayerRingBurst(victimPlayer, 5); | ||||
| 			P_DamageMobj(victim, shield, attacker, 1, DMG_WHUMBLE); | ||||
| 
 | ||||
| 			K_DropPowerUps(victimPlayer); | ||||
|  | @ -1293,8 +1294,15 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) | |||
| 
 | ||||
| 	auto doStumble = [](mobj_t *t1, mobj_t *t2) | ||||
| 	{ | ||||
| 		K_StumblePlayer(t2->player); | ||||
| 		K_SpawnAmps(t1->player, K_PvPAmpReward(20, t1->player, t2->player), t2); | ||||
| 		if (gametyperules & GTR_BUMPERS) | ||||
| 		{ | ||||
| 			K_StumblePlayer(t2->player); | ||||
| 			K_SpawnAmps(t1->player, K_PvPAmpReward(20, t1->player, t2->player), t2); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			P_DamageMobj(t2, t1, t1, 1, DMG_WHUMBLE); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	if (forEither(shouldStumble, doStumble)) | ||||
|  | @ -1325,7 +1333,7 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) | |||
| 
 | ||||
| 		bool stung = false; | ||||
| 
 | ||||
| 		if (RINGTOTAL(t2->player) <= 0 && t2->health == 1 && !(t2->player->pflags2 & PF2_UNSTINGABLE)) | ||||
| 		if (RINGTOTAL(t2->player) <= 0 && t2->player->ringboostinprogress == 0 && t2->health == 1 && !(t2->player->pflags2 & PF2_UNSTINGABLE)) | ||||
| 		{ | ||||
| 			P_DamageMobj(t2, t1, t1, 1, DMG_STING|DMG_WOMBO); | ||||
| 			// CONS_Printf("T2 stung\n");
 | ||||
|  |  | |||
|  | @ -477,10 +477,8 @@ void K_HandleFollower(player_t *player) | |||
| 
 | ||||
| 		if (fl->mode == FOLLOWERMODE_GROUND) | ||||
| 		{ | ||||
| 			sector_t *sec = R_PointInSubsector(sx, sy)->sector; | ||||
| 
 | ||||
| 			fh = min(fh, P_GetFloorZ(player->follower, sec, sx, sy, NULL)); | ||||
| 			ch = max(ch, P_GetCeilingZ(player->follower, sec, sx, sy, NULL) - ourheight); | ||||
| 			fh = min(fh, P_FloorzAtPos(sx, sy, player->follower->z, ourheight)); | ||||
| 			ch = max(ch, P_CeilingzAtPos(sx, sy, player->follower->z, ourheight) - ourheight); | ||||
| 
 | ||||
| 			if (P_IsObjectOnGround(player->mo) == false) | ||||
| 			{ | ||||
|  |  | |||
|  | @ -60,7 +60,7 @@ INT16 K_CalculateGPRankPoints(UINT16 exp, UINT8 position, UINT8 numplayers) | |||
| { | ||||
| 	INT16 points; | ||||
| 
 | ||||
| 	if (position >= numplayers || position == 0) | ||||
| 	if (position > numplayers || position == 0) | ||||
| 	{ | ||||
| 		// Invalid position, no points
 | ||||
| 		return 0; | ||||
|  | @ -858,7 +858,12 @@ void K_RetireBots(void) | |||
| 	std::stable_sort(humans.begin(), humans.end(), CompareReplacements); | ||||
| 	std::stable_sort(bots.begin(), bots.end(), CompareReplacements); | ||||
| 
 | ||||
| 	if (G_GametypeHasSpectators() == true && grandprixinfo.gp == false && cv_shuffleloser.value != 0) | ||||
| 	// If a player spectated mid-race or mid-duel, they will be placed in-game by K_CheckSpectateStatus,
 | ||||
| 	// and their position will be set to 0. Since we're only replacing one player as of now, there's no need
 | ||||
| 	// to do anything; a player has already been replaced.
 | ||||
| 	bool player_already_joined = (!humans.empty() && humans[0]->position == 0); | ||||
| 
 | ||||
| 	if (G_GametypeHasSpectators() == true && grandprixinfo.gp == false && !player_already_joined && cv_shuffleloser.value != 0) | ||||
| 	{ | ||||
| 		// While joiners and players still exist, insert joiners.
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,6 +22,26 @@ | |||
| #include "s_sound.h" | ||||
| #include "m_easing.h" | ||||
| 
 | ||||
| // Use for adding hitlag that should be mostly ignored by impervious players.
 | ||||
| // (Currently only called in power clash, but in the future...?)
 | ||||
| void K_AddHitLagFromCollision(mobj_t *mo, INT32 tics) | ||||
| { | ||||
| 	boolean doAnything = true; | ||||
| 
 | ||||
| 	if (mo->player == NULL || mo->type != MT_PLAYER) | ||||
| 		doAnything = false; | ||||
| 	else if (!K_PlayerCanPunt(mo->player)) | ||||
| 		doAnything = false; | ||||
| 
 | ||||
| 	if (!doAnything) | ||||
| 	{ | ||||
| 		K_AddHitLag(mo, tics, false); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	K_AddHitLag(mo, min(tics, 2), false); | ||||
| } | ||||
| 
 | ||||
| /*--------------------------------------------------
 | ||||
| 	void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage) | ||||
| 
 | ||||
|  | @ -34,9 +54,6 @@ void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage) | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (mo->player && mo->player->overshield) | ||||
| 		tics = min(tics, 3); | ||||
| 
 | ||||
| 	mo->hitlag += tics; | ||||
| 	mo->hitlag = min(mo->hitlag, MAXHITLAGTICS); | ||||
| 
 | ||||
|  |  | |||
|  | @ -26,6 +26,8 @@ extern "C" { | |||
| #define NUM_HITLAG_STATES (9) | ||||
| #define NUM_HITLAG_SOUNDS (4) | ||||
| 
 | ||||
| void K_AddHitLagFromCollision(mobj_t *mo, INT32 tics); | ||||
| 
 | ||||
| /*--------------------------------------------------
 | ||||
| 	void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										120
									
								
								src/k_hud.cpp
									
										
									
									
									
								
							
							
						
						
									
										120
									
								
								src/k_hud.cpp
									
										
									
									
									
								
							|  | @ -3307,19 +3307,13 @@ static void K_drawKartEmeralds(void) | |||
| 
 | ||||
| INT32 K_GetTransFlagFromFixed(fixed_t value, boolean midrace) | ||||
| { | ||||
| 	fixed_t base = midrace ? GRADINGFACTORSOFTCAP : FRACUNIT; | ||||
| 	fixed_t base = FRACUNIT; | ||||
| 
 | ||||
|     value = std::clamp(value, base - FRACUNIT/2, base + FRACUNIT/2); | ||||
| 
 | ||||
|     // Calculate distance from "base""
 | ||||
|     fixed_t distance = abs(base - value); | ||||
| 
 | ||||
| 	if (midrace) | ||||
| 	{ | ||||
| 		if (value > base) | ||||
| 			distance = FixedMul(distance, GRADINGFACTORCAPSTRENGTH); | ||||
| 	} | ||||
| 
 | ||||
| 	distance = std::clamp(distance, 0, FRACUNIT/2); | ||||
| 
 | ||||
|     // Map the distance to 0-10 range (10 = closest to 1.0, 0 = farthest from 1.0)
 | ||||
|  | @ -4241,11 +4235,20 @@ static boolean K_drawKartLaps(void) | |||
| 			K_DrawMarginSticker(fr-1+(flipflag ? 2 : 0), fy+1, 25+bump, V_HUDTRANS|V_SLIDEIN|splitflags, true, flipflag); | ||||
| 										// WHAT IS THIS?
 | ||||
| 										// WHAT ARE YOU FUCKING TALKING ABOUT?
 | ||||
| 		V_DrawMappedPatch(fr, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_exp[1], R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MUSTARD, GTC_CACHE)); | ||||
| 		auto transflag = K_GetTransFlagFromFixed(K_EffectiveGradingFactor(stplyr), true); | ||||
| 		skincolornum_t overlaycolor = K_EffectiveGradingFactor(stplyr) < FRACUNIT ? SKINCOLOR_RUBY : SKINCOLOR_ULTRAMARINE ; | ||||
| 		auto colormap = R_GetTranslationColormap(TC_RAINBOW, overlaycolor, GTC_CACHE); | ||||
| 		V_DrawMappedPatch(fr, fy, transflag|V_SLIDEIN|splitflags, kp_exp[1], colormap); | ||||
| 
 | ||||
| 		if (franticitems) | ||||
| 		{ | ||||
| 			V_DrawMappedPatch(fr, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_exp[1], R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PURPLE, GTC_CACHE)); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			V_DrawMappedPatch(fr, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_exp[1], R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MUSTARD, GTC_CACHE)); | ||||
| 			auto transflag = K_GetTransFlagFromFixed(K_EffectiveGradingFactor(stplyr), true); | ||||
| 			skincolornum_t overlaycolor = K_EffectiveGradingFactor(stplyr) < FRACUNIT ? SKINCOLOR_RUBY : SKINCOLOR_ULTRAMARINE ; | ||||
| 			auto colormap = R_GetTranslationColormap(TC_RAINBOW, overlaycolor, GTC_CACHE); | ||||
| 			V_DrawMappedPatch(fr, fy, transflag|V_SLIDEIN|splitflags, kp_exp[1], colormap); | ||||
| 		} | ||||
| 
 | ||||
| 
 | ||||
| 		// EXP
 | ||||
| 
 | ||||
|  | @ -4263,12 +4266,19 @@ static boolean K_drawKartLaps(void) | |||
| 		if (!drewsticker) | ||||
| 			K_DrawSticker(LAPS_X+13, LAPS_Y+5, 25+bump, V_HUDTRANS|V_SLIDEIN|splitflags, false); | ||||
| 
 | ||||
| 		V_DrawMappedPatch(LAPS_X+bump, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_exp[0], R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MUSTARD, GTC_CACHE)); | ||||
| 
 | ||||
| 		auto transflag = K_GetTransFlagFromFixed(K_EffectiveGradingFactor(stplyr), true); | ||||
| 		skincolornum_t overlaycolor = K_EffectiveGradingFactor(stplyr) < FRACUNIT ? SKINCOLOR_RUBY : SKINCOLOR_ULTRAMARINE ; | ||||
| 		auto colormap = R_GetTranslationColormap(TC_RAINBOW, overlaycolor, GTC_CACHE); | ||||
| 		V_DrawMappedPatch(LAPS_X+bump, LAPS_Y, transflag|V_SLIDEIN|splitflags, kp_exp[0], colormap); | ||||
| 		if (franticitems) | ||||
| 		{ | ||||
| 			V_DrawMappedPatch(LAPS_X+bump, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_exp[0], R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PURPLE, GTC_CACHE)); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			V_DrawMappedPatch(LAPS_X+bump, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_exp[0], R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MUSTARD, GTC_CACHE)); | ||||
| 			auto transflag = K_GetTransFlagFromFixed(K_EffectiveGradingFactor(stplyr), true); | ||||
| 			skincolornum_t overlaycolor = K_EffectiveGradingFactor(stplyr) < FRACUNIT ? SKINCOLOR_RUBY : SKINCOLOR_ULTRAMARINE ; | ||||
| 			auto colormap = R_GetTranslationColormap(TC_RAINBOW, overlaycolor, GTC_CACHE); | ||||
| 			V_DrawMappedPatch(LAPS_X+bump, LAPS_Y, transflag|V_SLIDEIN|splitflags, kp_exp[0], colormap); | ||||
| 		} | ||||
| 
 | ||||
| 		using srb2::Draw; | ||||
| 		Draw row = Draw(LAPS_X+23+bump, LAPS_Y+3).flags(V_HUDTRANS|V_SLIDEIN|splitflags|danceflag).font(Draw::Font::kThinTimer).colorize(dancecolor); | ||||
|  | @ -4304,6 +4314,20 @@ static void K_drawRingCounter(boolean gametypeinfoshown) | |||
| 	boolean colorring = false; | ||||
| 	INT32 ringx = 0, fy = 0; | ||||
| 
 | ||||
| 	UINT16 superringcolor = SKINCOLOR_SAPPHIRE; | ||||
| 	if (stplyr->momentboost) | ||||
| 	{ | ||||
| 		if (!cv_reducevfx.value) | ||||
| 		{ | ||||
| 			if (leveltime % 2) | ||||
| 				superringcolor = SKINCOLOR_GOLD; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			superringcolor = SKINCOLOR_GOLD; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	rn[0] = ((abs(stplyr->hudrings) / 10) % 10); | ||||
| 	rn[1] = (abs(stplyr->hudrings) % 10); | ||||
| 
 | ||||
|  | @ -4456,7 +4480,7 @@ static void K_drawRingCounter(boolean gametypeinfoshown) | |||
| 		if (stplyr->superringdisplay && !(stplyr->superringalert % 2)) | ||||
| 		{ | ||||
| 			using srb2::Draw; | ||||
| 			Draw row = Draw(fr+19+superoffset, fy).flags(V_HUDTRANS|V_SLIDEIN|splitflags).font(Draw::Font::kPing).colorize(SKINCOLOR_SAPPHIRE); | ||||
| 			Draw row = Draw(fr+19+superoffset, fy).flags(V_HUDTRANS|V_SLIDEIN|splitflags).font(Draw::Font::kPing).colorize(superringcolor); | ||||
| 			row.text("+{:01}", abs(stplyr->superringdisplay)); | ||||
| 		} | ||||
| 	} | ||||
|  | @ -4563,7 +4587,7 @@ static void K_drawRingCounter(boolean gametypeinfoshown) | |||
| 		if (stplyr->superringdisplay && !(stplyr->superringalert % 2)) | ||||
| 		{ | ||||
| 			using srb2::Draw; | ||||
| 			Draw row = Draw(LAPS_X+23+3+15, fy-4).flags(V_HUDTRANS|V_SLIDEIN|splitflags).font(Draw::Font::kThinTimer).colorize(SKINCOLOR_SAPPHIRE); | ||||
| 			Draw row = Draw(LAPS_X+23+3+15, fy-4).flags(V_HUDTRANS|V_SLIDEIN|splitflags).font(Draw::Font::kThinTimer).colorize(superringcolor); | ||||
| 			row.text("+{:01}", abs(stplyr->superringdisplay)); | ||||
| 		} | ||||
| 	} | ||||
|  | @ -5125,7 +5149,7 @@ static void K_drawKartWanted(void) | |||
| 
 | ||||
| static void K_drawKartPlayerCheck(void) | ||||
| { | ||||
| 	const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); | ||||
| 	const fixed_t maxdistance = FixedMul(2000 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); | ||||
| 	UINT8 i; | ||||
| 	INT32 splitflags = V_SNAPTOBOTTOM|V_SPLITSCREEN; | ||||
| 	fixed_t y = CHEK_Y * FRACUNIT; | ||||
|  | @ -5183,13 +5207,15 @@ static void K_drawKartPlayerCheck(void) | |||
| 
 | ||||
| 		distance = R_PointToDist2(pPos.x, pPos.y, v.x, v.y); | ||||
| 
 | ||||
| 		colormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(checkplayer->mo->color), GTC_CACHE); | ||||
| 
 | ||||
| 		if (distance > maxdistance) | ||||
| 		{ | ||||
| 			// Too far away
 | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if ((checkplayer->invincibilitytimer <= 0) && (leveltime & 2)) | ||||
| 		if ((checkplayer->invincibilitytimer <= 0) && (leveltime & 2) && !(cv_reducevfx.value)) | ||||
| 		{ | ||||
| 			pnum++; // white frames
 | ||||
| 		} | ||||
|  | @ -5202,12 +5228,18 @@ static void K_drawKartPlayerCheck(void) | |||
| 		{ | ||||
| 			pnum += 2; | ||||
| 		} | ||||
| 		else if ((checkplayer->instaWhipCharge) && !(cv_reducevfx.value)) | ||||
| 		{ | ||||
| 			if (leveltime & 2) | ||||
| 				R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(SKINCOLOR_WHITE), GTC_CACHE); | ||||
| 			else | ||||
| 				R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(SKINCOLOR_BLACK), GTC_CACHE); | ||||
| 		} | ||||
| 
 | ||||
| 		K_ObjectTracking(&result, &v, true); | ||||
| 
 | ||||
| 		if (result.onScreen == true) | ||||
| 		{ | ||||
| 			colormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(checkplayer->mo->color), GTC_CACHE); | ||||
| 			V_DrawFixedPatch(result.x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN|splitflags, kp_check[pnum], colormap); | ||||
| 		} | ||||
| 	} | ||||
|  | @ -6833,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; | ||||
|  | @ -7172,11 +7210,13 @@ static void K_drawLapStartAnim(void) | |||
| 			oldval = (188 + (32 * std::max(0, progressOld - 76))) * FRACUNIT; | ||||
| 			interpx = R_InterpolateFixed(oldval, newval); | ||||
| 
 | ||||
| 			UINT32 sanitizedlaps = std::min((UINT32)99, (UINT32)stplyr->latestlap); | ||||
| 
 | ||||
| 			V_DrawFixedPatch( | ||||
| 				interpx, // 194
 | ||||
| 				30*FRACUNIT, // 24
 | ||||
| 				FRACUNIT, V_SNAPTOTOP|hudtransflags, | ||||
| 				kp_lapanim_number[(((UINT32)stplyr->latestlap) / 10)][std::min(progress/2-8, 2)], NULL); | ||||
| 				kp_lapanim_number[(sanitizedlaps / 10)][std::min(progress/2-8, 2)], NULL); | ||||
| 
 | ||||
| 			if (progress/2-10 >= 0) | ||||
| 			{ | ||||
|  | @ -7188,7 +7228,7 @@ static void K_drawLapStartAnim(void) | |||
| 					interpx, // 221
 | ||||
| 					30*FRACUNIT, // 24
 | ||||
| 					FRACUNIT, V_SNAPTOTOP|hudtransflags, | ||||
| 					kp_lapanim_number[(((UINT32)stplyr->latestlap) % 10)][std::min(progress/2-10, 2)], NULL); | ||||
| 					kp_lapanim_number[(sanitizedlaps % 10)][std::min(progress/2-10, 2)], NULL); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | @ -7311,7 +7351,21 @@ static void K_drawDistributionDebugger(void) | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	K_FillItemRouletteData(stplyr, &rouletteData, false, true); | ||||
| 	{ | ||||
| 		// GROSS GROSS GROSS GROSS copypaste from K_FillItemRoulette
 | ||||
| 		// but without the potential for Lua side-effects etc.
 | ||||
| 		// This sucks the ass.
 | ||||
| 		K_InitRoulette(&rouletteData); | ||||
| 
 | ||||
| 		rouletteData.baseDist = K_UndoMapScaling(stplyr->distancetofinish); | ||||
| 
 | ||||
| 		if (stplyr->pflags & PF_AUTOROULETTE) | ||||
| 			rouletteData.autoroulette = true; | ||||
| 
 | ||||
| 		K_CalculateRouletteSpeed(&rouletteData); | ||||
| 
 | ||||
| 		K_FillItemRouletteData(stplyr, &rouletteData, false, true); | ||||
| 	} | ||||
| 
 | ||||
| 	if (cv_kartdebugdistribution.value <= 1) | ||||
| 		return; | ||||
|  | @ -7772,7 +7826,7 @@ void K_drawKartHUD(void) | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (staffsync) | ||||
| 	if (staffsync && staffsync_total) | ||||
| 	{ | ||||
| 		V_DrawFadeScreen(31, 8); | ||||
| 		V_DrawCenteredGamemodeString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 - 30, 0, 0, "Staff Ghost Sync Test"); | ||||
|  | @ -7970,8 +8024,16 @@ void K_drawKartHUD(void) | |||
| 
 | ||||
| 				std::string arrow = (ahead == 1 || ahead == -2) ? "(" : ")"; | ||||
| 
 | ||||
| 				// vibes offset
 | ||||
| 				row.x(-35).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); | ||||
| 				if (skin != -1) | ||||
| 				{ | ||||
| 					// vibes offset
 | ||||
| 					row.x(-35).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					ahead = -1; | ||||
| 				} | ||||
| 
 | ||||
| 
 | ||||
| 				if (pos > 1) | ||||
| 					row.x(-35).font(Draw::Font::kPing).text(va("%d", pos)); | ||||
|  |  | |||
							
								
								
									
										727
									
								
								src/k_kart.c
									
										
									
									
									
								
							
							
						
						
									
										727
									
								
								src/k_kart.c
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										23
									
								
								src/k_kart.h
									
										
									
									
									
								
							
							
						
						
									
										23
									
								
								src/k_kart.h
									
										
									
									
									
								
							|  | @ -41,8 +41,8 @@ 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 PUNISHWINDOW (10*TICRATE/10) | ||||
| #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
 | ||||
| #define BAIL_DROP (FRACUNIT) // How many rings it has to drop before stun starts
 | ||||
|  | @ -57,6 +57,8 @@ Make sure this matches the actual number of states | |||
| #define MINCOMBOFLOAT (mapobjectscale*1) | ||||
| #define MAXCOMBOTIME (TICRATE*4) | ||||
| 
 | ||||
| #define STARTBOOST_DURATION (125) | ||||
| 
 | ||||
| #define TIMEATTACK_START (TICRATE*10) | ||||
| 
 | ||||
| #define LIGHTNING_CHARGE (TICRATE*2) | ||||
|  | @ -76,7 +78,7 @@ Make sure this matches the actual number of states | |||
| 
 | ||||
| #define EARLY_ITEM_FLICKER (NUMTRANSMAPS) | ||||
| 
 | ||||
| #define TRIPWIRE_OK_SOUND (sfx_s3k40) | ||||
| #define TRIPWIRE_OK_SOUND (sfx_sonbo2) | ||||
| #define TRIPWIRE_NG_SOUND (sfx_gshaf) | ||||
| 
 | ||||
| // 2023-08-26 +ang20 to Sal's OG values to make them friendlier - Tyron
 | ||||
|  | @ -105,7 +107,7 @@ Make sure this matches the actual number of states | |||
| #define MAXTOPACCEL (12*FRACUNIT) | ||||
| #define TOPACCELREGEN (FRACUNIT/16) | ||||
| 
 | ||||
| #define BUBBLESCAM (4) | ||||
| #define BUBBLESCAM (4*FRACUNIT) | ||||
| 
 | ||||
| // Handling boosts and sliptide conditions got weird.
 | ||||
| // You must be under a handling boost of at least SLIPTIDEHANDLING to sliptide.
 | ||||
|  | @ -123,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); | ||||
|  | @ -132,11 +139,10 @@ boolean K_DuelItemAlwaysSpawns(mapthing_t *mt); | |||
| boolean K_InRaceDuel(void); | ||||
| player_t *K_DuelOpponent(player_t *player); | ||||
| 
 | ||||
| fixed_t K_FinalCheckpointPower(void); | ||||
| fixed_t K_EffectiveGradingFactor(const player_t *player); | ||||
| #define MINGRADINGFACTOR (FRACUNIT/2) | ||||
| #define MINFRANTICFACTOR (8*FRACUNIT/10) | ||||
| #define GRADINGFACTORSOFTCAP (FRACUNIT) | ||||
| #define GRADINGFACTORCAPSTRENGTH (3*FRACUNIT) | ||||
| 
 | ||||
| void K_TimerReset(void); | ||||
| void K_TimerInit(void); | ||||
|  | @ -145,7 +151,7 @@ UINT32 K_GetPlayerDontDrawFlag(player_t *player); | |||
| void K_ReduceVFXForEveryone(mobj_t *mo); | ||||
| 
 | ||||
| boolean K_IsPlayerLosing(player_t *player); | ||||
| fixed_t K_PlayerScamPercentage(const player_t *player, UINT8 mult); | ||||
| fixed_t K_PlayerScamPercentage(const player_t *player, fixed_t mult); | ||||
| fixed_t K_GetKartGameSpeedScalar(SINT8 value); | ||||
| 
 | ||||
| INT32 K_GetShieldFromItem(INT32 item); | ||||
|  | @ -296,6 +302,7 @@ boolean K_PlayerEBrake(const player_t *player); | |||
| boolean K_PlayerGuard(const player_t *player); | ||||
| SINT8 K_Sliptiding(const player_t *player); | ||||
| boolean K_FastFallBounce(player_t *player); | ||||
| void K_DappleEmployment(player_t *player); | ||||
| fixed_t K_PlayerBaseFriction(const player_t *player, fixed_t original); | ||||
| void K_AdjustPlayerFriction(player_t *player); | ||||
| void K_MoveKartPlayer(player_t *player, boolean onground); | ||||
|  | @ -347,7 +354,7 @@ boolean K_ThunderDome(void); | |||
| 
 | ||||
| boolean K_PlayerCanUseItem(player_t *player); | ||||
| 
 | ||||
| fixed_t K_GetGradingFactorAdjustment(player_t *player); | ||||
| fixed_t K_GetGradingFactorAdjustment(player_t *player, UINT32 gradingpoint); | ||||
| fixed_t K_GetGradingFactorMinMax(player_t *player, boolean max); | ||||
| UINT16 K_GetEXP(player_t *player); | ||||
| 
 | ||||
|  |  | |||
|  | @ -661,7 +661,7 @@ typedef enum | |||
| 	MA_NO | ||||
| } manswer_e; | ||||
| 
 | ||||
| #define MAXMENUMESSAGE 256 | ||||
| #define MAXMENUMESSAGE 448 | ||||
| #define MENUMESSAGECLOSE 2 | ||||
| extern struct menumessage_s | ||||
| { | ||||
|  | @ -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); | ||||
|  | @ -769,6 +769,7 @@ void M_StartControlPanel(void); | |||
| void M_ValidateRestoreMenu(void); | ||||
| menu_t *M_SpecificMenuRestore(menu_t *torestore); | ||||
| void M_ClearMenus(boolean callexitmenufunc); | ||||
| void M_ClearMenusNoTitle(boolean callexitmenufunc); | ||||
| void M_SelectableClearMenus(INT32 choice); | ||||
| void M_SetupNextMenu(menu_t *menudef, boolean nofade); | ||||
| void M_GoBack(INT32 choice); | ||||
|  | @ -960,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; | ||||
|  |  | |||
|  | @ -2315,13 +2315,24 @@ static void M_DrawCharSelectCursor(UINT8 num) | |||
| 	} | ||||
| 	else if (p->mdepth > CSSTEP_CHARS) | ||||
| 	{ | ||||
| 		V_DrawMappedPatch(x, y, 0, W_CachePatchName(selectframesa[setup_animcounter % SELECTLEN], PU_CACHE), colormap); | ||||
| 		if (selectframesb[(setup_animcounter-1) % SELECTLEN] != NULL) | ||||
| 			V_DrawMappedPatch(x, y, V_TRANSLUCENT, W_CachePatchName(selectframesb[(setup_animcounter-1) % SELECTLEN], PU_CACHE), colormap); | ||||
| 		if (cv_reducevfx.value) | ||||
| 		{ | ||||
| 			V_DrawMappedPatch(x, y, 0, W_CachePatchName(selectframesa[0], PU_CACHE), colormap); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			V_DrawMappedPatch(x, y, 0, W_CachePatchName(selectframesa[setup_animcounter % SELECTLEN], PU_CACHE), colormap); | ||||
| 			if (selectframesb[(setup_animcounter-1) % SELECTLEN] != NULL) | ||||
| 				V_DrawMappedPatch(x, y, V_TRANSLUCENT, W_CachePatchName(selectframesb[(setup_animcounter-1) % SELECTLEN], PU_CACHE), colormap); | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		V_DrawMappedPatch(x, y, 0, W_CachePatchName(idleframes[setup_animcounter % IDLELEN], PU_CACHE), colormap); | ||||
| 		if (cv_reducevfx.value) | ||||
| 			V_DrawMappedPatch(x, y, 0, W_CachePatchName(idleframes[0], PU_CACHE), colormap); | ||||
| 		else | ||||
| 			V_DrawMappedPatch(x, y, 0, W_CachePatchName(idleframes[setup_animcounter % IDLELEN], PU_CACHE), colormap); | ||||
| 	} | ||||
| 
 | ||||
| 	if (p->mdepth < CSSTEP_READY) | ||||
|  | @ -3329,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) | ||||
| 	{ | ||||
|  | @ -5659,22 +5685,25 @@ void M_DrawProfileControls(void) | |||
| 					help = va("6Bt. (Auto): Tries to guess your 6-button pad's layout."); | ||||
| 					break; | ||||
| 				case 4: | ||||
| 					help = va("6Bt. (A): Saturn buttons, Retro-Bit Wired DInput layout."); | ||||
| 					help = va("6Bt. (A): Saturn (Retro-Bit Wired DInput) - C/Z = RB/RT"); | ||||
| 					break; | ||||
| 				case 5: | ||||
| 					help = va("6Bt. (B): Saturn buttons, Retro-Bit Wireless DInput layout."); | ||||
| 					help = va("6Bt. (B): Saturn (Retro-Bit Wireless DInput) - C/Z = LB/RB"); | ||||
| 					break; | ||||
| 				case 6: | ||||
| 					help = va("6Bt. (C): Saturn buttons, Retro-Bit XInput layout."); | ||||
| 					help = va("6Bt. (C): Saturn (Retro-Bit XInput) - C/Z = RT/LT"); | ||||
| 					break; | ||||
| 				case 7: | ||||
| 					help = va("6Bt. (D): Saturn buttons, arcade/8BitDo layout. (C/Z = RT/RB)"); | ||||
| 					help = va("6Bt. (D): Saturn (arcade / 8BitDo) - C/Z = RT/RB"); | ||||
| 					break; | ||||
| 				case 8: | ||||
| 					help = va("6Bt. (E): Saturn buttons, Hori/M30X layout. (LB/LT = LS/RS)"); | ||||
| 					help = va("6Bt. (E): Saturn (Hori/M30X) - C/Z = RT/RB, LB/LT = LS/RS"); | ||||
| 					break; | ||||
| 				case 9: | ||||
| 					help = va("6Bt. (F): Saturn buttons, Mayflash layout. (C/Z = RS/LS)"); | ||||
| 					help = va("6Bt. (F): Saturn (Mayflash) - C/Z = RS/LS"); | ||||
| 					break; | ||||
| 				case 10: | ||||
| 					help = va("6Bt. (G): Saturn (orig M30) -  C/Z = RB/LB"); | ||||
| 					break; | ||||
| 			} | ||||
| 
 | ||||
|  | @ -6092,6 +6121,14 @@ static char *M_GetGameplayMode(void) | |||
| 		return va("Relaxed"); | ||||
| 	} | ||||
| 
 | ||||
| 	if (franticitems) | ||||
| 	{ | ||||
| 		if (cv_4thgear.value) | ||||
| 			return va("4th Gear! Frantic!"); | ||||
| 		else | ||||
| 			return va("Gear %d - Frantic\n", gamespeed+1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (cv_4thgear.value) | ||||
| 		return va("4th Gear!"); | ||||
| 
 | ||||
|  |  | |||
|  | @ -912,10 +912,6 @@ void M_ClearMenus(boolean callexitmenufunc) | |||
| 	if (currentMenu->quitroutine && callexitmenufunc && !currentMenu->quitroutine()) | ||||
| 		return; // we can't quit this menu (also used to set parameter from the menu)
 | ||||
| 
 | ||||
| #ifndef DC // Save the config file. I'm sick of crashing the game later and losing all my changes!
 | ||||
| 	COM_BufAddText(va("saveconfig \"%s\" -silent\n", configfile)); | ||||
| #endif //Alam: But not on the Dreamcast's VMUs
 | ||||
| 
 | ||||
| 	currentMenu->lastOn = itemOn; | ||||
| 
 | ||||
| 	if (gamestate == GS_MENU) // Back to title screen
 | ||||
|  | @ -934,6 +930,24 @@ void M_ClearMenus(boolean callexitmenufunc) | |||
| 	menuactive = false; | ||||
| } | ||||
| 
 | ||||
| void M_ClearMenusNoTitle(boolean callexitmenufunc) | ||||
| { | ||||
| 	if (!menuactive) | ||||
| 		return; | ||||
| 
 | ||||
| 	CON_ClearHUD(); | ||||
| 
 | ||||
| 	if (currentMenu->quitroutine && callexitmenufunc && !currentMenu->quitroutine()) | ||||
| 		return; // we can't quit this menu (also used to set parameter from the menu)
 | ||||
| 
 | ||||
| 	currentMenu->lastOn = itemOn; | ||||
| 
 | ||||
| 	M_AbortVirtualKeyboard(); | ||||
| 	menumessage.active = false; | ||||
| 
 | ||||
| 	menuactive = false; | ||||
| } | ||||
| 
 | ||||
| void M_SelectableClearMenus(INT32 choice) | ||||
| { | ||||
| 	(void)choice; | ||||
|  | @ -1070,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); | ||||
| 
 | ||||
|  | @ -1083,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; } | ||||
|  |  | |||
|  | @ -384,14 +384,20 @@ void K_UpdatePowerLevels(player_t *player, UINT8 gradingpoint, boolean forfeit) | |||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			if (exitBonus == false) | ||||
| 			fixed_t prevInc = ourinc; | ||||
| 
 | ||||
| 			INT16 dvs = max(K_GetNumGradingPoints(), 1); | ||||
| 			ourinc = FixedDiv(ourinc, dvs*FRACUNIT); | ||||
| 			theirinc = FixedDiv(theirinc, dvs*FRACUNIT); | ||||
| 
 | ||||
| 			if (exitBonus) | ||||
| 			{ | ||||
| 				ourinc = FixedMul(ourinc, FRACUNIT + K_FinalCheckpointPower()); | ||||
| 				theirinc = FixedMul(theirinc, FRACUNIT + K_FinalCheckpointPower()); | ||||
| 				CONS_Debug(DBG_PWRLV, "Final check bonus (%d / %d * %d = %d)\n", prevInc/FRACUNIT, dvs, K_FinalCheckpointPower(), ourinc/FRACUNIT); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				fixed_t prevInc = ourinc; | ||||
| 
 | ||||
| 				INT16 dvs = max(K_GetNumGradingPoints(), 1); | ||||
| 				ourinc = FixedDiv(ourinc, dvs*FRACUNIT); | ||||
| 				theirinc = FixedDiv(theirinc, dvs*FRACUNIT); | ||||
| 
 | ||||
| 				CONS_Debug(DBG_PWRLV, "Reduced (%d / %d = %d) because it's not the end of the race\n", prevInc/FRACUNIT, dvs, ourinc/FRACUNIT); | ||||
| 			} | ||||
| 		} | ||||
|  | @ -432,6 +438,8 @@ void K_UpdatePowerLevelsFinalize(player_t *player, boolean onForfeit) | |||
| 
 | ||||
| 	if (checksleft <= 0) | ||||
| 	{ | ||||
| 		if (!(gametyperules & GTR_CHECKPOINTS)) // We should probably do at least _one_ PWR update.
 | ||||
| 			K_UpdatePowerLevels(player, player->gradingpointnum, onForfeit); | ||||
| 		// We've done every checkpoint already.
 | ||||
| 		return; | ||||
| 	} | ||||
|  |  | |||
|  | @ -423,8 +423,8 @@ static void K_DrawFinishLineBeamForLine(fixed_t offset, angle_t aiming, line_t * | |||
| --------------------------------------------------*/ | ||||
| 
 | ||||
| void K_RunFinishLineBeam(void) | ||||
| { | ||||
| 	if ((gametyperules & GTR_ROLLINGSTART) || !(leveltime < starttime || rainbowstartavailable == true)) | ||||
| {	 | ||||
| 	if ((gametyperules & GTR_ROLLINGSTART) || !(leveltime < starttime || rainbowstartavailable == true) || P_LevelIsFrozen()) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
|  |  | |||
|  | @ -577,9 +577,8 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) | |||
| 	// If our last map was Special, check for "uncredited" continues to offset the rank bump.
 | ||||
| 	fixed_t hiddenpercent = percent; | ||||
| 	gpRank_level_t *lastgrade = &rankData->levels[rankData->numLevels - 1]; | ||||
| 	UINT32 id = lastgrade->id; | ||||
| 
 | ||||
| 	if (rankData->specialWon && (mapheaderinfo[id-1]->typeoflevel & G_TOLFlag(GT_SPECIAL))) | ||||
| 	if (rankData->specialWon) | ||||
| 	{ | ||||
| 		hiddenpercent -= FRACUNIT / RANK_CONTINUE_PENALTY_DIV * lastgrade->continues; | ||||
| 	} | ||||
|  |  | |||
|  | @ -176,6 +176,12 @@ void K_DoIngameRespawn(player_t *player) | |||
| 		K_DoFault(player); | ||||
| 	} | ||||
| 
 | ||||
| 	if (player->rings <= -20 && !player->respawn.fromRingShooter) | ||||
| 	{ | ||||
| 		P_KillMobj(player->mo, NULL, NULL, DMG_INSTAKILL); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	player->ringboost = 0; | ||||
| 	player->driftboost = player->strongdriftboost = 0; | ||||
| 	player->gateBoost = 0; | ||||
|  | @ -740,7 +746,7 @@ static void K_DropDashWait(player_t *player) | |||
| 		player->respawn.timer--; | ||||
| 
 | ||||
| 	if (player->pflags & PF_FAULT) | ||||
| 		return;	 | ||||
| 		return; | ||||
| 
 | ||||
| 	if (leveltime % 8 == 0) | ||||
| 	{ | ||||
|  |  | |||
							
								
								
									
										100
									
								
								src/k_roulette.c
									
										
									
									
									
								
							
							
						
						
									
										100
									
								
								src/k_roulette.c
									
										
									
									
									
								
							|  | @ -80,7 +80,7 @@ | |||
| static UINT32 K_DynamicItemOddsRace[NUMKARTRESULTS-1][2] = | ||||
| { | ||||
| 	// distance, duplication tolerance
 | ||||
| 	{20, 10}, // sneaker
 | ||||
| 	{25, 10}, // sneaker
 | ||||
| 	{63, 12}, // rocketsneaker
 | ||||
| 	{60, 19}, // invincibility
 | ||||
| 	{8, 4}, // banana
 | ||||
|  | @ -491,12 +491,7 @@ UINT32 K_ScaleItemDistance(INT32 distance, UINT8 numPlayers) | |||
| 	return distance; | ||||
| } | ||||
| 
 | ||||
| /*--------------------------------------------------
 | ||||
| 	static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) | ||||
| 
 | ||||
| 		See header file for description. | ||||
| --------------------------------------------------*/ | ||||
| UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) | ||||
| static UINT32 K_GetUnscaledFirstDistance(const player_t *player) | ||||
| { | ||||
| 	UINT32 pdis = 0; | ||||
| 
 | ||||
|  | @ -546,6 +541,19 @@ UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) | |||
| 	} | ||||
| 
 | ||||
| 	pdis = K_UndoMapScaling(pdis); | ||||
| 
 | ||||
| 	return pdis; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*--------------------------------------------------
 | ||||
| 	static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) | ||||
| 
 | ||||
| 		See header file for description. | ||||
| --------------------------------------------------*/ | ||||
| UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) | ||||
| { | ||||
| 	UINT32 pdis = K_GetUnscaledFirstDistance(player); | ||||
| 	pdis = K_ScaleItemDistance(pdis, numPlayers); | ||||
| 
 | ||||
| 	if (player->bot && (player->botvars.rival || cv_levelskull.value)) | ||||
|  | @ -735,7 +743,7 @@ boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette) | |||
| 	Return:- | ||||
| 		N/A | ||||
| --------------------------------------------------*/ | ||||
| static void K_InitRoulette(itemroulette_t *const roulette) | ||||
| void K_InitRoulette(itemroulette_t *const roulette) | ||||
| { | ||||
| 	size_t i; | ||||
| 
 | ||||
|  | @ -1047,7 +1055,7 @@ ATTRUNUSED static boolean K_IsItemUselessAlone(kartitems_t item) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| ATTRUNUSED static boolean K_IsItemSpeed(kartitems_t item) | ||||
| static boolean K_IsItemSpeed(kartitems_t item) | ||||
| { | ||||
| 	switch (item) | ||||
| 	{ | ||||
|  | @ -1059,6 +1067,7 @@ ATTRUNUSED static boolean K_IsItemSpeed(kartitems_t item) | |||
| 		case KRITEM_TRIPLESNEAKER: | ||||
| 		case KITEM_FLAMESHIELD: | ||||
| 		case KITEM_SHRINK: | ||||
| 		case KITEM_SUPERRING: | ||||
| 			return true; | ||||
| 		default: | ||||
| 			return false; | ||||
|  | @ -1411,11 +1420,6 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo | |||
| 	roulette->preexpdist = K_GetItemRouletteDistance(player, roulette->playing); | ||||
| 	roulette->dist = roulette->preexpdist; | ||||
| 
 | ||||
| 	if ((gametyperules & GTR_CIRCUIT) && !K_Cooperative()) | ||||
| 	{ | ||||
| 		roulette->dist = FixedMul(roulette->preexpdist, K_EffectiveGradingFactor(player)); | ||||
| 	} | ||||
| 
 | ||||
| 	// ===============================================================================
 | ||||
| 	// Dynamic Roulette. Oh boy!
 | ||||
| 	// Alright, here's the broad plan:
 | ||||
|  | @ -1426,15 +1430,41 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo | |||
| 	// 5: Skim any items that are much weaker than the reel's average out of the roulette
 | ||||
| 	// 6: Cram it all in
 | ||||
| 
 | ||||
| 	fixed_t largegamescaler = roulette->playing * 14 + 100; // Spread out item odds in large games for a less insane experience.
 | ||||
| 	UINT32 targetpower = 100 * roulette->dist / largegamescaler; // fill roulette with items around this value!
 | ||||
| 
 | ||||
| 	UINT32 powers[NUMKARTRESULTS]; // how strong is each item? think of this as a "target distance" for this item to spawn at
 | ||||
| 	UINT32 deltas[NUMKARTRESULTS]; // how different is that strength from target?
 | ||||
| 	UINT32 candidates[NUMKARTRESULTS]; // how many of this item should we try to insert?
 | ||||
| 	UINT32 dupetolerance[NUMKARTRESULTS]; // how willing are we to select this item after already selecting it? higher values = lower dupe penalty
 | ||||
| 	boolean permit[NUMKARTRESULTS]; // is this item allowed?
 | ||||
| 
 | ||||
| 	UINT32 lonelinessSuppressor = DISTVAR; // This close to 1st? Dampen loneliness (you have a target!)
 | ||||
| 	UINT32 maxEXPDistanceCut = 3*DISTVAR; // The maximum amount you can be displaced by EXP
 | ||||
| 
 | ||||
| 	// If we're too close to 1st in absolute units, crush our top-end item odds down.
 | ||||
| 	fixed_t crowdingFirst = 0; | ||||
| 	if (player->position != 1) | ||||
| 		crowdingFirst = FixedRescale(K_GetUnscaledFirstDistance(player), 0, 4*DISTVAR, Easing_InCubic, FRACUNIT, 0); | ||||
| 
 | ||||
| 	if ((gametyperules & GTR_CIRCUIT) && !K_Cooperative()) | ||||
| 	{ | ||||
| 		roulette->dist = FixedMul(roulette->preexpdist, K_EffectiveGradingFactor(player)); | ||||
| 
 | ||||
| 		if (roulette->dist < roulette->preexpdist) | ||||
| 		{ | ||||
| 			if (roulette->preexpdist - roulette->dist > maxEXPDistanceCut) | ||||
| 			{ | ||||
| 				roulette->dist = roulette->preexpdist - maxEXPDistanceCut; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fixed_t largegamescaler = roulette->playing * 11 + 115; // Spread out item odds in large games for a less insane experience.
 | ||||
| 	if (franticitems) | ||||
| 		largegamescaler = 100; // Except in Frantic, where you know what you're getting
 | ||||
| 
 | ||||
| 	UINT32 targetpower = 100 * roulette->dist / largegamescaler; // fill roulette with items around this value!
 | ||||
| 	if (!(specialstageinfo.valid)) | ||||
| 		targetpower = Easing_Linear(crowdingFirst, targetpower, targetpower/2); | ||||
| 
 | ||||
| 	boolean rival = (player->bot && (player->botvars.rival || cv_levelskull.value)); | ||||
| 	boolean filterweakitems = true; // strip unusually weak items from reel?
 | ||||
| 	UINT8 reelsize = 15; // How many items to attempt to add in prepass?
 | ||||
|  | @ -1488,6 +1518,10 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo | |||
| 		{ | ||||
| 			powers[i] = humanscaler * K_DynamicItemOddsRace[i-1][0]; | ||||
| 			dupetolerance[i] = K_DynamicItemOddsRace[i-1][1]; | ||||
| 
 | ||||
| 			// Bias towards attack items when close to the leader, gotta work for the slingshot pass!
 | ||||
| 			if (K_IsItemSpeed(i) && i != KITEM_SUPERRING) | ||||
| 				powers[i] = Easing_Linear(crowdingFirst, powers[i], 2*powers[i]); | ||||
| 		} | ||||
| 
 | ||||
| 		maxpower = max(maxpower, powers[i]); | ||||
|  | @ -1586,6 +1620,12 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo | |||
| 
 | ||||
| 		loneliness = Easing_InCubic(loneliness, 0, FRACUNIT); | ||||
| 
 | ||||
| 		// You are not lonely if you're super close to 1st, even if 3nd is far away.
 | ||||
| 		if (roulette->preexpdist < lonelinessSuppressor) | ||||
| 		{ | ||||
| 			loneliness = FixedRescale(roulette->preexpdist, 0, lonelinessSuppressor, Easing_InCubic, 0, loneliness); | ||||
| 		} | ||||
| 
 | ||||
| 		// Give interaction items a nudge against initial selection if you're lonely..
 | ||||
| 		for (i = 1; i < NUMKARTRESULTS; i++) | ||||
| 		{ | ||||
|  | @ -1621,6 +1661,13 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo | |||
| 	UINT8 added = 0; // How many items added so far?
 | ||||
| 	UINT32 totalreelpower = 0; // How much total item power in the reel? Used for an average later.
 | ||||
| 
 | ||||
| 	UINT32 basepenalty = 4*DISTVAR; // How much to penalize repicked items, to ensure item variety.
 | ||||
| 	// BUT, keep the item distribution tighter if we're close to the frontrunner...
 | ||||
| 	UINT32 penalty = Easing_Linear(crowdingFirst, basepenalty, basepenalty/2); | ||||
| 	if (player->position == 1) // ...unless we ARE the frontrunner.
 | ||||
| 		penalty = basepenalty; | ||||
| 
 | ||||
| 
 | ||||
| 	for (i = 0; i < reelsize; i++) | ||||
| 	{ | ||||
| 		UINT32 lowestdelta = INT32_MAX; | ||||
|  | @ -1647,7 +1694,7 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo | |||
| 		// Impose a penalty to this item's delta, to bias against selecting it again.
 | ||||
| 		// This is naively slashed by an item's "duplicate tolerance":
 | ||||
| 		// lower tolerance means that an item is less likely to be reselected (it's "rarer").
 | ||||
| 		UINT32 deltapenalty = 4*DISTVAR*(1+candidates[bestitem])/dupetolerance[bestitem]; | ||||
| 		UINT32 deltapenalty = penalty*(1+candidates[bestitem])/dupetolerance[bestitem]; | ||||
| 
 | ||||
| 		// Power items get better odds in frantic, or if you're the rival.
 | ||||
| 		// (For the rival, this is way more likely to matter at lower skills, where they're
 | ||||
|  | @ -1703,6 +1750,7 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo | |||
| 
 | ||||
| 	UINT8 debugcount = 0; // For the "simple" odds debugger.
 | ||||
| 	UINT32 meanreelpower = totalreelpower/max(added, 1); // Average power for the "moth filter".
 | ||||
| 	UINT32 maxreduction = -1 * min(2 * DISTVAR, meanreelpower/2); | ||||
| 
 | ||||
| 	// == PREP FOR ADDING TO THE ROULETTE REEL
 | ||||
| 	// Sal's prior work for this is rock-solid.
 | ||||
|  | @ -1718,7 +1766,7 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo | |||
| 
 | ||||
| 		// If we're far away from interactions, be extra aggressive about tossing attack items.
 | ||||
| 		if (filterweakitems && !reject && !K_IsItemSpeed(i)) | ||||
| 			reject = (powers[i] + Easing_Linear(loneliness, DISTVAR, -2 * DISTVAR) < meanreelpower); | ||||
| 			reject = (powers[i] + Easing_Linear(loneliness, DISTVAR, maxreduction) < meanreelpower); | ||||
| 
 | ||||
| 		// Popcorn Super Ring is always strong enough, we put it there on purpose.
 | ||||
| 		if (i == KITEM_SUPERRING && !canfiltersuperring) | ||||
|  | @ -1731,10 +1779,13 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo | |||
| 			UINT16 BASE_X = 280; | ||||
| 			UINT16 BASE_Y = 5+12*debugcount; | ||||
| 			INT32 FLAGS = V_SNAPTOTOP|V_SNAPTORIGHT; | ||||
| 			V_DrawRightAlignedThinString(BASE_X - 12, 5, FLAGS, va("%d", targetpower/humanscaler)); | ||||
| 			V_DrawRightAlignedThinString(BASE_X - 12, 5+12, FLAGS, va("%d", toFront)); | ||||
| 			V_DrawRightAlignedThinString(BASE_X - 12, 5+24, FLAGS, va("%d", toBack)); | ||||
| 			V_DrawRightAlignedThinString(BASE_X - 12, 5+36, FLAGS, va("%d", loneliness)); | ||||
| 
 | ||||
| 			if (reject) | ||||
| 				FLAGS |= V_TRANSLUCENT; | ||||
| 			V_DrawRightAlignedThinString(BASE_X - 12, 5, FLAGS, va("TP %d", targetpower/humanscaler)); | ||||
| 			V_DrawRightAlignedThinString(BASE_X - 12, 5+12, FLAGS, va("FB %d / %d", toFront, toBack)); | ||||
| 			V_DrawRightAlignedThinString(BASE_X - 12, 5+24, FLAGS, va("L %d / CF %d", loneliness, crowdingFirst)); | ||||
| 			V_DrawRightAlignedThinString(BASE_X - 12, 5+36, FLAGS, va("D %d / %d", roulette->preexpdist, roulette->dist)); | ||||
| 			for(UINT8 k = 0; k < candidates[i]; k++) | ||||
| 				V_DrawFixedPatch((BASE_X + 3*k)*FRACUNIT, (BASE_Y-7)*FRACUNIT, (FRACUNIT >> 1), FLAGS, K_GetSmallStaticCachedItemPatch(i), NULL); | ||||
| 			UINT8 amount = K_ItemResultToAmount(i, roulette); | ||||
|  | @ -1905,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(); | ||||
|  |  | |||
|  | @ -138,6 +138,19 @@ UINT32 K_UndoMapScaling(UINT32 distance); | |||
| 		N/A | ||||
| --------------------------------------------------*/ | ||||
| 
 | ||||
| void K_InitRoulette(itemroulette_t *const roulette); | ||||
| /*--------------------------------------------------
 | ||||
| 	static void K_InitRoulette(itemroulette_t *const roulette) | ||||
| 
 | ||||
| 		Initializes the data for a new item roulette. | ||||
| 
 | ||||
| 	Input Arguments:- | ||||
| 		roulette - The item roulette data to initialize. | ||||
| 
 | ||||
| 	Return:- | ||||
| 		N/A | ||||
| --------------------------------------------------*/ | ||||
| 
 | ||||
| void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item); | ||||
| 
 | ||||
| /*--------------------------------------------------
 | ||||
|  |  | |||
|  | @ -225,7 +225,7 @@ INT32 level_tally_t::CalculateGrade(void) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	const INT32 positionWeight = (position > 0 && numPlayers > 2) ? 50 : 0; | ||||
| 	const INT32 positionWeight =  0; // (position > 0 && numPlayers > 2) ? 50 : 0;
 | ||||
| 	const INT32 total = positionWeight + bonusWeights[0] + bonusWeights[1]; | ||||
| 
 | ||||
| 	INT32 ours = 0; | ||||
|  | @ -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) | ||||
|  |  | |||
|  | @ -501,7 +501,21 @@ void K_ProcessTerrainEffect(mobj_t *mo) | |||
| 	if (terrain->damageType > 0) | ||||
| 	{ | ||||
| 		UINT8 dmg = (terrain->damageType & 0xFF); | ||||
| 		P_DamageMobj(mo, NULL, NULL, 1, dmg); | ||||
| 
 | ||||
| 		if ((dmg == DMG_STUMBLE) && !G_CompatLevel(0x0010)) | ||||
| 		{ | ||||
| 			if (player->mo->hitlag == 0 && | ||||
| 				(player->mo->momz == 0 || (player->mo->momz > 0) != (P_MobjFlip(player->mo) > 0))) | ||||
| 			{ | ||||
| 				player->pflags2 |= PF2_ALWAYSDAMAGED; | ||||
| 				P_DamageMobj(mo, NULL, NULL, 1, dmg); | ||||
| 				player->pflags2 &= ~PF2_ALWAYSDAMAGED; | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			P_DamageMobj(mo, NULL, NULL, 1, dmg); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Sneaker panel
 | ||||
|  | @ -589,6 +603,15 @@ void K_ProcessTerrainEffect(mobj_t *mo) | |||
| 			player->dashpadcooldown = TICRATE/3; | ||||
| 			player->trickpanel = TRICKSTATE_NONE; | ||||
| 			player->floorboost = 2; | ||||
| 			 | ||||
| 			if (G_CompatLevel(0x0011)) | ||||
| 			{ | ||||
| 				// Old behavior, no grease
 | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				player->tiregrease = TICRATE/2; | ||||
| 			} | ||||
| 
 | ||||
| 			S_StartSound(player->mo, sfx_cdfm62); | ||||
| 		} | ||||
|  | @ -2011,7 +2034,7 @@ static boolean K_TERRAINLumpParser(char *data, size_t size) | |||
| 			Z_Free(tkn); | ||||
| 			tkn = M_GetToken(NULL); | ||||
| 			pos = M_GetTokenPos(); | ||||
| 			 | ||||
| 
 | ||||
| 			if (tkn && pos <= size) | ||||
| 			{ | ||||
| 				if (stricmp(tkn, "optional") == 0) | ||||
|  |  | |||
|  | @ -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" | ||||
|  | @ -252,6 +253,16 @@ static const struct { | |||
| 	{META_FOOTSTEP,     "t_footstep_t"}, | ||||
| 	{META_OVERLAY,      "t_overlay_t"}, | ||||
| 	{META_TERRAIN,      "terrain_t"}, | ||||
| 	 | ||||
| 	{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} | ||||
| }; | ||||
|  | @ -476,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); | ||||
| 
 | ||||
|  | @ -3987,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)); | ||||
|  | @ -5266,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)); | ||||
|  | @ -5290,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)); | ||||
|  | @ -5986,7 +6149,15 @@ static int lib_kSetNameForBot(lua_State *L) | |||
| 	if (!player->bot) | ||||
| 		return luaL_error(L, "You may only change bot names."); | ||||
| 
 | ||||
| 	K_SetNameForBot(player-players, realname); | ||||
| 	// Doing this to avoid a discarded const warning:
 | ||||
| 	char modifiedname[MAXPLAYERNAME+1] = ""; | ||||
| 	strcpy(modifiedname, realname); | ||||
| 
 | ||||
| 	if (!IsPlayerNameGood(modifiedname)) | ||||
| 		return luaL_error(L, "Invalid bot name - it must be between %d and %d characters of length, " | ||||
| 			"not start with a space, @ or ~ characters, and it must be composed of valid ASCII characters.", 1, MAXPLAYERNAME);	 | ||||
| 
 | ||||
| 	K_SetNameForBot(player-players, modifiedname); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -6774,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}, | ||||
|  | @ -6883,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}, | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,7 +23,9 @@ enum botvars { | |||
| 	botvars_difficulty, | ||||
| 	botvars_diffincrease, | ||||
| 	botvars_rival, | ||||
| 	botvars_foe, | ||||
| 	botvars_rubberband, | ||||
| 	botvars_bumpslow, | ||||
| 	botvars_itemdelay, | ||||
| 	botvars_itemconfirm, | ||||
| 	botvars_turnconfirm, | ||||
|  | @ -31,6 +33,9 @@ enum botvars { | |||
| 	botvars_respawnconfirm, | ||||
| 	botvars_roulettepriority, | ||||
| 	botvars_roulettetimeout, | ||||
| 	botvars_predictionerror, | ||||
| 	botvars_recentdeflection, | ||||
| 	botvars_lastangle | ||||
| }; | ||||
| 
 | ||||
| static const char *const botvars_opt[] = { | ||||
|  | @ -39,7 +44,9 @@ static const char *const botvars_opt[] = { | |||
| 	"difficulty", | ||||
| 	"diffincrease", | ||||
| 	"rival", | ||||
| 	"foe", | ||||
| 	"rubberband", | ||||
| 	"bumpslow", | ||||
| 	"itemdelay", | ||||
| 	"itemconfirm", | ||||
| 	"turnconfirm", | ||||
|  | @ -47,6 +54,9 @@ static const char *const botvars_opt[] = { | |||
| 	"respawnconfirm", | ||||
| 	"roulettepriority", | ||||
| 	"roulettetimeout", | ||||
| 	"predictionerror", | ||||
| 	"recentdeflection", | ||||
| 	"lastangle", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
|  | @ -77,9 +87,15 @@ static int botvars_get(lua_State *L) | |||
| 	case botvars_rival: | ||||
| 		lua_pushboolean(L, botvars->rival); | ||||
| 		break; | ||||
| 	case botvars_foe: | ||||
| 		lua_pushboolean(L, botvars->foe); | ||||
| 		break; | ||||
| 	case botvars_rubberband: | ||||
| 		lua_pushfixed(L, botvars->rubberband); | ||||
| 		break; | ||||
| 	case botvars_bumpslow: | ||||
| 		lua_pushinteger(L, botvars->bumpslow); | ||||
| 		break; | ||||
| 	case botvars_itemdelay: | ||||
| 		lua_pushinteger(L, botvars->itemdelay); | ||||
| 		break; | ||||
|  | @ -101,6 +117,15 @@ static int botvars_get(lua_State *L) | |||
| 	case botvars_roulettetimeout: | ||||
| 		lua_pushinteger(L, botvars->rouletteTimeout); | ||||
| 		break; | ||||
| 	case botvars_predictionerror: | ||||
| 		lua_pushinteger(L, botvars->predictionError); | ||||
| 		break; | ||||
| 	case botvars_recentdeflection: | ||||
| 		lua_pushinteger(L, botvars->recentDeflection); | ||||
| 		break; | ||||
| 	case botvars_lastangle: | ||||
| 		lua_pushinteger(L, botvars->lastAngle); | ||||
| 		break; | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
|  | @ -133,9 +158,15 @@ static int botvars_set(lua_State *L) | |||
| 	case botvars_rival: | ||||
| 		botvars->rival = luaL_checkboolean(L, 3); | ||||
| 		break; | ||||
| 	case botvars_foe: | ||||
| 		botvars->foe = luaL_checkboolean(L, 3); | ||||
| 		break; | ||||
| 	case botvars_rubberband: | ||||
| 		botvars->rubberband = luaL_checkfixed(L, 3); | ||||
| 		break; | ||||
| 	case botvars_bumpslow: | ||||
| 		botvars->bumpslow = luaL_checkinteger(L, 3); | ||||
| 		break; | ||||
| 	case botvars_itemdelay: | ||||
| 		botvars->itemdelay = luaL_checkinteger(L, 3); | ||||
| 		break; | ||||
|  | @ -157,6 +188,15 @@ static int botvars_set(lua_State *L) | |||
| 	case botvars_roulettetimeout: | ||||
| 		botvars->rouletteTimeout = luaL_checkinteger(L, 3); | ||||
| 		break; | ||||
| 	case botvars_predictionerror: | ||||
| 		botvars->predictionError = luaL_checkangle(L, 3); | ||||
| 		break; | ||||
| 	case botvars_recentdeflection: | ||||
| 		botvars->recentDeflection = luaL_checkangle(L, 3); | ||||
| 		break; | ||||
| 	case botvars_lastangle: | ||||
| 		botvars->lastAngle = luaL_checkangle(L, 3); | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										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; | ||||
| } | ||||
|  | @ -28,6 +28,7 @@ enum itemroulette { | |||
| 	itemroulette_itemlist, | ||||
| 	itemroulette_playing, | ||||
| 	itemroulette_exiting, | ||||
| 	itemroulette_preexpdist, | ||||
| 	itemroulette_dist, | ||||
| 	itemroulette_basedist, | ||||
| 	itemroulette_firstdist, | ||||
|  | @ -41,7 +42,8 @@ enum itemroulette { | |||
| 	itemroulette_eggman, | ||||
| 	itemroulette_ringbox, | ||||
| 	itemroulette_autoroulette, | ||||
| 	itemroulette_reserved | ||||
| 	itemroulette_reserved, | ||||
| 	itemroulette_popcorn | ||||
| }; | ||||
| static const char *const itemroulette_opt[] = { | ||||
| 	"valid", | ||||
|  | @ -49,6 +51,7 @@ static const char *const itemroulette_opt[] = { | |||
| 	"itemlist", | ||||
| 	"playing", | ||||
| 	"exiting", | ||||
| 	"preexpdist", | ||||
| 	"dist", | ||||
| 	"basedist", | ||||
| 	"firstdist", | ||||
|  | @ -63,6 +66,7 @@ static const char *const itemroulette_opt[] = { | |||
| 	"ringbox", | ||||
| 	"autoroulette", | ||||
| 	"reserved", | ||||
| 	"popcorn", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
|  | @ -91,6 +95,9 @@ static int itemroulette_get(lua_State *L) | |||
| 	case itemroulette_exiting: | ||||
| 		lua_pushinteger(L, itemroulette->exiting); | ||||
| 		break; | ||||
| 	case itemroulette_preexpdist: | ||||
| 		lua_pushinteger(L, itemroulette->preexpdist); | ||||
| 		break; | ||||
| 	case itemroulette_dist: | ||||
| 		lua_pushinteger(L, itemroulette->dist); | ||||
| 		break; | ||||
|  | @ -133,6 +140,9 @@ static int itemroulette_get(lua_State *L) | |||
| 	case itemroulette_reserved: | ||||
| 		lua_pushinteger(L, itemroulette->reserved); | ||||
| 		break; | ||||
| 	case itemroulette_popcorn: | ||||
| 		lua_pushinteger(L, itemroulette->popcorn); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return 1; | ||||
|  | @ -172,6 +182,9 @@ static int itemroulette_set(lua_State *L) | |||
| 	case itemroulette_exiting: | ||||
| 		itemroulette->exiting = luaL_checkinteger(L, 3); | ||||
| 		break; | ||||
| 	case itemroulette_preexpdist: | ||||
| 		itemroulette->preexpdist = luaL_checkinteger(L, 3); | ||||
| 		break; | ||||
| 	case itemroulette_dist: | ||||
| 		itemroulette->dist = luaL_checkinteger(L, 3); | ||||
| 		break; | ||||
|  | @ -214,6 +227,9 @@ static int itemroulette_set(lua_State *L) | |||
| 	case itemroulette_reserved: | ||||
| 		itemroulette->reserved = luaL_checkinteger(L, 3); | ||||
| 		break; | ||||
| 	case itemroulette_popcorn: | ||||
| 		itemroulette->popcorn = luaL_checkinteger(L, 3); | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -118,6 +118,16 @@ extern lua_State *gL; | |||
| #define META_OVERLAY "T_OVERLAY_T*" | ||||
| #define META_TERRAIN "TERRAIN_T*" | ||||
| 
 | ||||
| #define META_POWERUPVARS "POWERUPVARS_T*" | ||||
| #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); | ||||
|  | @ -142,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"
 | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -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)\ | ||||
|  |  | |||
|  | @ -113,8 +113,13 @@ static int skin_get(lua_State *L) | |||
| 		lua_pushinteger(L, skin->highresscale); | ||||
| 		break; | ||||
| 	case skin_rivals: | ||||
| 		// This would be pretty cool to push
 | ||||
| 		return UNIMPLEMENTED; | ||||
| 		lua_createtable(L, SKINRIVALS, 0); | ||||
| 		for (size_t i = 0; i < SKINRIVALS; i++) | ||||
| 		{ | ||||
| 			lua_pushstring(L, skin->rivals[i]); | ||||
| 			lua_rawseti(L, -2, 1 + i); | ||||
| 		} | ||||
| 		break; | ||||
| 	case skin_soundsid: | ||||
| 		LUA_PushUserdata(L, skin->soundsid, META_SOUNDSID); | ||||
| 		break; | ||||
|  |  | |||
|  | @ -1797,7 +1797,7 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) | |||
| 		case UCRP_PREFIX_BONUSROUND: | ||||
| 			return ((grandprixinfo.gp == true) && (grandprixinfo.eventmode == GPEVENT_BONUS)); | ||||
| 		case UCRP_PREFIX_TIMEATTACK: | ||||
| 			return (modeattacking != ATTACKING_NONE); | ||||
| 			return (modeattacking != ATTACKING_NONE && !(skins[player->skin]->flags & SF_HIVOLT)); | ||||
| 		case UCRP_PREFIX_PRISONBREAK: | ||||
| 			return ((gametyperules & GTR_PRISONS) && battleprisons); | ||||
| 		case UCRP_PREFIX_SEALEDSTAR: | ||||
|  | @ -1843,8 +1843,7 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) | |||
| 			if (cn->extrainfo2 != 0) | ||||
| 				return (K_PodiumGrade() >= cn->extrainfo1); | ||||
| 			if (cn->extrainfo1 != 0) | ||||
| 				return (player->position != 0 | ||||
| 					&& player->position <= cn->extrainfo1); | ||||
| 				return K_GetPodiumPosition(player) <= cn->extrainfo1; | ||||
| 			return true; | ||||
| 		case UCRP_PODIUMEMERALD: | ||||
| 		case UCRP_PODIUMPRIZE: | ||||
|  |  | |||
|  | @ -32,6 +32,11 @@ | |||
| // Extended map support.
 | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| #if defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) | ||||
| #include <immintrin.h> | ||||
| #define NEED_INTEL_DENORMAL_BIT 1 | ||||
| #endif | ||||
| 
 | ||||
| #include "doomdef.h" | ||||
| #include "g_game.h" | ||||
| #include "m_misc.h" | ||||
|  | @ -2805,3 +2810,44 @@ const char * M_Ftrim (double f) | |||
| 		return &dig[1];/* skip the 0 */ | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /** Enable floating point denormal-to-zero section, if necessary */ | ||||
| floatdenormalstate_t M_EnterFloatDenormalToZero(void) | ||||
| { | ||||
| #ifdef NEED_INTEL_DENORMAL_BIT | ||||
| 	floatdenormalstate_t state = 0; | ||||
| 	state |= _MM_GET_FLUSH_ZERO_MODE() == _MM_FLUSH_ZERO_ON ? 1 : 0; | ||||
| 	state |= _MM_GET_DENORMALS_ZERO_MODE() == _MM_DENORMALS_ZERO_ON ? 2 : 0; | ||||
| 
 | ||||
| 	if ((state & 1) == 0) | ||||
| 	{ | ||||
| 		_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); | ||||
| 	} | ||||
| 	if ((state & 2) == 0) | ||||
| 	{ | ||||
| 		_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); | ||||
| 	} | ||||
| 	return state; | ||||
| #else | ||||
| 	return 0; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /** Exit floating point denormal-to-zero section, if necessary, restoring previous state */ | ||||
| void M_ExitFloatDenormalToZero(floatdenormalstate_t previous) | ||||
| { | ||||
| #ifdef NEED_INTEL_DENORMAL_BIT | ||||
| 	if ((previous & 1) == 0) | ||||
| 	{ | ||||
| 		_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_OFF); | ||||
| 	} | ||||
| 	if ((previous & 2) == 0) | ||||
| 	{ | ||||
| 		_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_OFF); | ||||
| 	} | ||||
| 	return; | ||||
| #else | ||||
| 	(void)previous; | ||||
| 	return; | ||||
| #endif | ||||
| } | ||||
|  |  | |||
|  | @ -167,6 +167,13 @@ FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size); | |||
| 
 | ||||
| extern char configfile[MAX_WADPATH]; | ||||
| 
 | ||||
| typedef INT32 floatdenormalstate_t; | ||||
| 
 | ||||
| /** Enable floating point denormal-to-zero section, if necessary */ | ||||
| floatdenormalstate_t M_EnterFloatDenormalToZero(void); | ||||
| /** Exit floating point denormal-to-zero section, if necessary, restoring previous state */ | ||||
| void M_ExitFloatDenormalToZero(floatdenormalstate_t previous); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } // extern "C"
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -61,6 +61,8 @@ int ps_checkposition_calls = 0; | |||
| precise_t ps_lua_thinkframe_time = 0; | ||||
| int ps_lua_mobjhooks = 0; | ||||
| 
 | ||||
| precise_t ps_voiceupdatetime = 0; | ||||
| 
 | ||||
| // dynamically allocated resizeable array for thinkframe hook stats
 | ||||
| ps_hookinfo_t *thinkframe_hooks = NULL; | ||||
| int thinkframe_hooks_length = 0; | ||||
|  | @ -208,6 +210,7 @@ static void M_DrawRenderStats(void) | |||
| 
 | ||||
| 	perfstatrow_t tictime_row[] = { | ||||
| 		{"logic  ", "Game logic:    ", &ps_tictime}, | ||||
| 		{"voice  ", "Voice update:  ", &ps_voiceupdatetime}, | ||||
| 		{0} | ||||
| 	}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -45,6 +45,8 @@ extern int       ps_checkposition_calls; | |||
| extern precise_t ps_lua_thinkframe_time; | ||||
| extern int       ps_lua_mobjhooks; | ||||
| 
 | ||||
| extern precise_t ps_voiceupdatetime; | ||||
| 
 | ||||
| struct ps_hookinfo_t | ||||
| { | ||||
| 	precise_t time_taken; | ||||
|  |  | |||
							
								
								
									
										107
									
								
								src/m_pw.cpp
									
										
									
									
									
								
							
							
						
						
									
										107
									
								
								src/m_pw.cpp
									
										
									
									
									
								
							|  | @ -32,6 +32,7 @@ | |||
| #include "g_game.h" | ||||
| #include "k_menu.h" | ||||
| #include "m_cheat.h" | ||||
| #include "m_random.h" | ||||
| #include "m_cond.h" | ||||
| #include "m_pw.h" | ||||
| #include "m_pw_hash.h" | ||||
|  | @ -364,6 +365,111 @@ void f_maps() | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void f_tutorials() | ||||
| { | ||||
| 	UINT16 i; | ||||
| 	boolean success = false; | ||||
| 
 | ||||
| 	for (i = 0; i < MAXUNLOCKABLES; i++) | ||||
| 	{ | ||||
| 		if (!unlockables[i].conditionset) | ||||
| 			continue; | ||||
| 		if (unlockables[i].conditionset == CH_FURYBIKE) | ||||
| 			continue; | ||||
| 		if (gamedata->unlocked[i]) | ||||
| 			continue; | ||||
| 		if (unlockables[i].type != SECRET_MAP) | ||||
| 			continue; | ||||
| 
 | ||||
| 		UINT16 mapnum = M_UnlockableMapNum(&unlockables[i]); | ||||
| 		if (mapnum >= nummapheaders || !mapheaderinfo[mapnum]) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (G_GuessGametypeByTOL(mapheaderinfo[mapnum]->typeoflevel) != GT_TUTORIAL) | ||||
| 			continue; | ||||
| 
 | ||||
| 		gamedata->unlocked[i] = true; | ||||
| 		success = true; | ||||
| 	} | ||||
| 
 | ||||
| 	if (success) | ||||
| 	{ | ||||
| 		S_StartSound(0, sfx_kc42); | ||||
| 		G_SaveGameData(); | ||||
| 	} | ||||
| 
 | ||||
| 	const char *knucklesrap; | ||||
| 	const UINT8 numsections = 5; | ||||
| 	static UINT8 section = numsections; | ||||
| 	if (section == numsections) | ||||
| 		section = M_RandomKey(numsections); | ||||
| 
 | ||||
| 	switch (section) | ||||
| 	{ | ||||
| 		default: | ||||
| 			knucklesrap = | ||||
| 				"\x85""So here's what I'm thinkin',                    \n" | ||||
| 				"\x85""        last time smart guys got together,\n" | ||||
| 				"\x85""In a tough sandy place                        \n" | ||||
| 				"\x85""                    with a lot of hot weather,\n" | ||||
| 				"\x85""Playin' fundamental forces                  \n" | ||||
| 				"\x85""                      at the top of their class,\n" | ||||
| 				"\x85""They made the sky so much hotter        \n" | ||||
| 				"\x85""                      and that sand into glass!\x80"; | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			knucklesrap = | ||||
| 				"\x85""But somethin's different, see?           \n" | ||||
| 				"\x85""My homie right there, I trust him trustin'\n" | ||||
| 				"\x85""             the Eggman like it's nothin',\n" | ||||
| 				"\x85""A smart little guy                     \n" | ||||
| 				"\x85""         with a brother I like fightin',\n" | ||||
| 				"\x85""If there's somethin' cooking           \n" | ||||
| 				"\x85""        I'm sure he'll do the right thing.\x80"; | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			knucklesrap = | ||||
| 				"\x85""I watched a space station fall,          \n" | ||||
| 				"\x85""       straight down, fast fall, into the ground,\n" | ||||
| 				"\x85""Pieces of fire, shooting stars,                 \n" | ||||
| 				"\x85""                      don't make a wish, kids\n" | ||||
| 				"\x85""Last second it's, gone, I ask how,            \n" | ||||
| 				"\x85""              behold, they call it chaos control,\n" | ||||
| 				"\x85""I don't know it, never heard it, seen it, felt it,  \n" | ||||
| 				"\x85""  sensed it. Even to me, these powers are a mystery.\x80"; | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			knucklesrap = | ||||
| 				"\x85""The tide goes out, it carries time away,  \n" | ||||
| 				"\x85""                     we call it yesterday,\n" | ||||
| 				"\x85""The tide comes in, it rings, it sings,    \n" | ||||
| 				"\x85""                      it brings a new age,\n" | ||||
| 				"\x85""But right now it's just me and the water,  \n" | ||||
| 				"\x85""       thoughts clear, future lookin' hotter,\n" | ||||
| 				"\x85""I let myself sink in, feel the waves,     \n" | ||||
| 				"\x85""                 feel my body get lighter.\x80"; | ||||
| 			break; | ||||
| 		case 4: | ||||
| 			knucklesrap = | ||||
| 				"\x85""Somethin' brewin' at the edges,            \n" | ||||
| 				"\x85""   that's what a ring is, nothin' but edges,\n" | ||||
| 				"\x85""Circled light in a band,                  \n" | ||||
| 				"\x85""                 hold 'em in in your hand,\n" | ||||
| 				"\x85""But it's a miracle a thousand times over,\n" | ||||
| 				"\x85""Small gasps of potential\n" | ||||
| 				"\x85""               floatin' over the sand.\x80"; | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	section = (section + 1) % numsections; | ||||
| 
 | ||||
| 	M_StartMessage("\"Broken Arrow\" ...for Sunbeam Paradise", | ||||
| 		va("\"%s\"\n\n%s", | ||||
| 		knucklesrap, | ||||
| 		(success ? "Unlocked all Tutorials." : "There are no more Tutorials to unlock.")), | ||||
| 		NULL, MM_NOTHING, NULL, NULL); | ||||
| } | ||||
| 
 | ||||
| void f_characters() | ||||
| { | ||||
| 	UINT16 i; | ||||
|  | @ -779,6 +885,7 @@ void M_PasswordInit(void) | |||
| 	passwords.emplace_back(f_colors, "aSk8dw6FzJtTEmovh8fVEtUBpu6lj3QlRT/B5lwiEhAw8dAhRBQLdvtYlPaQcZISWI4wneAfAo6w5d6uf5r++g=="); | ||||
| 	passwords.emplace_back(f_followers, "zYCIZw2qcnUbtF0P2ybLNHajdl8zrje0hzGex7yuMFe7fj4mvx4AegoMmvir28YvAbfAqkz/ekQRzr+RhrycHw=="); | ||||
| 	passwords.emplace_back(f_maps, "u/Svaf+DCnCpJ8xmP3AVP4CK6X6X4O3fY73cmIq88ZJEygwz+n+L66q4Vhlv13vWgld1PEyRszFErzflQt9WZw=="); | ||||
| 	passwords.emplace_back(f_tutorials, "G2FMttJpJ+lI/DeQu8tthL5i7AB4dk8uuksZR1c2N08Zrmpj3vTqWpbhxuSzSrhH10wJfWahR7QOgQdBkDbTdQ=="); | ||||
| 	passwords.emplace_back(f_characters, "MohmPqpaGSd3MEHLfQKUFl/Yg8pHE+12X1LHEP59Gs/5w1u8mPtGUXNv1GYTF+c8gQqT5hXpZ3FeZ/EfCxo34g=="); | ||||
| 	passwords.emplace_back(f_altmusic, "dZgxKNagOtB9F7wXqUUPzsuq4tfQlfK8ZqEeFXdI3Hd+k5tYfRm3ToLgbqawaNmwuLVrJ8PB+QnH4gT3ojnTMw=="); | ||||
| 	passwords.emplace_back(f_timeattack, "mFu5OB9d6jnc2kth7HE66wJ42F/GHDzSvuciK1Qw++6iGnpBccxcKjpoxgOvD3eIoqR606ruBINuXi23proXHQ=="); | ||||
|  |  | |||
|  | @ -21,7 +21,42 @@ | |||
| #include "m_random.h" | ||||
| #include "m_fixed.h" | ||||
| 
 | ||||
| 
 | ||||
| char rng_class_names[34][30] = { | ||||
| 	"UNDEFINED", | ||||
| 	"EXECUTOR", | ||||
| 	"ACS", | ||||
| 	"DECORATION", | ||||
| 	"TERRAIN", | ||||
| 	"BUBBLE", | ||||
| 	"RANDOMANIM", | ||||
| 	"PLAYERSTARTS", | ||||
| 	"VOICES", | ||||
| 	"RANDOMSKIN", | ||||
| 	"RANDOMAUDIENCE", | ||||
| 	"RULESCRAMBLE", | ||||
| 	"MUSICSELECT", | ||||
| 	"ITEM_ROULETTE", | ||||
| 	"ITEM_RINGS", | ||||
| 	"ITEM_SHRINK", | ||||
| 	"ITEM_BUBBLE", | ||||
| 	"ITEM_DEBRIS", | ||||
| 	"ITEM_BOOST", | ||||
| 	"EXPLOSION", | ||||
| 	"SMOLDERING", | ||||
| 	"SPARKLE", | ||||
| 	"MOVINGTARGET", | ||||
| 	"TRACKHAZARDD", | ||||
| 	"BATTLEUFO", | ||||
| 	"BOTS", | ||||
| 	"AUTOROULETTE", | ||||
| 	"FUZZ", | ||||
| 	"FROSTTHROWERS", | ||||
| 	"ITEM_SPAWNER", | ||||
| 	"TEAMS", | ||||
| 	"DUMMY", | ||||
| 	"INTERPHUDRANDOM", | ||||
| 	"NUISANCE" | ||||
| }; | ||||
| 
 | ||||
| // ---------------------------
 | ||||
| // RNG functions (not synched)
 | ||||
|  |  | |||
|  | @ -103,6 +103,7 @@ typedef enum | |||
| 	PRNUMCLASS | ||||
| } pr_class_t; | ||||
| 
 | ||||
| extern char rng_class_names[34][30]; | ||||
| // M_Random functions pull random numbers of various types that aren't network synced.
 | ||||
| // P_Random functions pulls random bytes from a PRNG that is network synced.
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -438,7 +438,7 @@ void EggTV::watch() const | |||
| 	{ | ||||
| 		restoreMenu = currentMenu; | ||||
| 
 | ||||
| 		M_ClearMenus(false); | ||||
| 		M_ClearMenusNoTitle(false); | ||||
| 
 | ||||
| 		demo.loadfiles = true; | ||||
| 		demo.ignorefiles = false; | ||||
|  |  | |||
|  | @ -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}, | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ menuitem_t OPTIONS_EditProfile[] = { | |||
| 	{IT_STRING | IT_CALL, "Controls", "Change the button mappings.", | ||||
| 		NULL, {.routine = M_ProfileDeviceSelect}, 0, 91}, | ||||
| 
 | ||||
| 	{IT_STRING | IT_SUBMENU, "Accessibility", "Acccessibility and quality of life options.", | ||||
| 	{IT_STRING | IT_SUBMENU, "Accessibility", "Accessibility and quality of life options.", | ||||
| 		NULL, {.submenu = &OPTIONS_ProfileAccessibilityDef}, 0, 111}, | ||||
| 
 | ||||
| 	{IT_STRING | IT_CALL, "Character", NULL, // tooltip set in M_StartEditProfile
 | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ menuitem_t OPTIONS_Voice[] = | |||
| 		NULL, srb2::itemaction(&cv_voice_inputamp), 0, 0}, | ||||
| 
 | ||||
| 	{IT_STRING | IT_CVAR, "Input Noise Suppression", "Suppress background noise from your voice.", | ||||
| 		NULL, {.cvar = &cv_voice_denoise}, 0, 0}, | ||||
| 		NULL, srb2::itemaction(&cv_voice_denoise), 0, 0}, | ||||
| 
 | ||||
| 	{IT_STRING | IT_CVAR, "Input Sensitivity", "Voice higher than this threshold will transmit, in decibels.", | ||||
| 		NULL, srb2::itemaction(&cv_voice_activationthreshold), 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; | ||||
|  | @ -440,7 +455,7 @@ void M_HandleStaffReplay(INT32 choice) | |||
| 		staffbrief_t *staffbrief; | ||||
| 		restoreMenu = &PLAY_TAReplayDef; | ||||
| 
 | ||||
| 		M_ClearMenus(true); | ||||
| 		M_ClearMenusNoTitle(true); | ||||
| 		demo.loadfiles = false; | ||||
| 		demo.ignorefiles = true; // Just assume that record attack replays have the files needed
 | ||||
| 
 | ||||
|  | @ -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) | ||||
| 	{ | ||||
|  | @ -507,7 +509,7 @@ void M_ReplayTimeAttack(INT32 choice) | |||
| 
 | ||||
| 	restoreMenu = &PLAY_TAReplayDef; | ||||
| 
 | ||||
| 	M_ClearMenus(true); | ||||
| 	M_ClearMenusNoTitle(true); | ||||
| 	demo.loadfiles = false; | ||||
| 	demo.ignorefiles = true; // Just assume that record attack replays have the files needed
 | ||||
| 
 | ||||
|  | @ -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
 | ||||
|  | @ -692,7 +658,7 @@ void M_StartTimeAttack(INT32 choice) | |||
| 		false | ||||
| 	); | ||||
| 
 | ||||
| 	M_ClearMenus(true); | ||||
| 	M_ClearMenusNoTitle(true); | ||||
| 
 | ||||
| 	G_UpdateTimeStickerMedals(levellist.choosemap, true); | ||||
| } | ||||
|  |  | |||
|  | @ -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"
 | ||||
|  |  | |||
|  | @ -292,7 +292,7 @@ void Obj_BulbTouched(mobj_t *special, mobj_t *toucher) | |||
| 	P_MoveOrigin(toucher, special->x, special->y, special->z); | ||||
| 	toucher->player->nocontrol = 1; | ||||
| 	P_SetTarget(&toucher->tracer, special); | ||||
| 	toucher->flags &= ~MF_SHOOTABLE; | ||||
| 	toucher->flags &= ~(MF_SHOOTABLE|MF_NOGRAVITY); | ||||
| 	toucher->renderflags |= RF_DONTDRAW; | ||||
| 	P_SetTarget(&special->target, toucher); | ||||
| 	special->extravalue1 = spd; | ||||
|  |  | |||
|  | @ -27,122 +27,125 @@ | |||
| 
 | ||||
| static void ghostme(mobj_t *exp, player_t *player) | ||||
| { | ||||
|     if (exp->cusval%2) | ||||
|         return; | ||||
| 	if (exp->cusval%2) | ||||
| 		return; | ||||
| 
 | ||||
|     mobj_t *ghost = P_SpawnGhostMobj(exp); | ||||
|     ghost->colorized = true; | ||||
|     ghost->color = player->skincolor; | ||||
|     ghost->renderflags |= RF_ADD; | ||||
|     ghost->fuse = 2; | ||||
| 	mobj_t *ghost = P_SpawnGhostMobj(exp); | ||||
| 	ghost->colorized = true; | ||||
| 	ghost->color = player->skincolor; | ||||
| 	ghost->renderflags |= RF_ADD; | ||||
| 	ghost->fuse = 2; | ||||
| } | ||||
| 
 | ||||
| void Obj_ExpThink (mobj_t *exp) | ||||
| { | ||||
|     if (P_MobjWasRemoved(exp->target) | ||||
| 	if (P_MobjWasRemoved(exp->target) | ||||
| 		|| exp->target->health == 0 | ||||
| 		|| exp->target->destscale <= 1 // sealed star fall out
 | ||||
| 		|| !exp->target->player) | ||||
|     { | ||||
|         P_RemoveMobj(exp); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         mobj_t *mo = exp->target; | ||||
|         player_t *player = mo->player; | ||||
| 	{ | ||||
| 		P_RemoveMobj(exp); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		mobj_t *mo = exp->target; | ||||
| 		player_t *player = mo->player; | ||||
| 
 | ||||
|         fixed_t dist, fakez; | ||||
|         angle_t hang, vang; | ||||
| 		fixed_t dist, fakez; | ||||
| 		angle_t hang, vang; | ||||
| 
 | ||||
|         dist = P_AproxDistance(P_AproxDistance(exp->x - mo->x, exp->y - mo->y), exp->z - mo->z); | ||||
| 		dist = P_AproxDistance(P_AproxDistance(exp->x - mo->x, exp->y - mo->y), exp->z - mo->z); | ||||
| 
 | ||||
|         exp->renderflags |= RF_DONTDRAW; | ||||
|         exp->renderflags &= ~K_GetPlayerDontDrawFlag(player); | ||||
| 		exp->renderflags |= RF_DONTDRAW; | ||||
| 		exp->renderflags &= ~K_GetPlayerDontDrawFlag(player); | ||||
| 
 | ||||
|         // K_MatchGenericExtraFlags(exp, mo);
 | ||||
| 		// K_MatchGenericExtraFlags(exp, mo);
 | ||||
| 
 | ||||
|         exp->cusval++; | ||||
| 		exp->cusval++; | ||||
| 
 | ||||
|         // bullshit copypaste orbit behavior
 | ||||
|         if (exp->threshold) | ||||
|         { | ||||
|             fixed_t orbit = (4*mo->scale) * (16 - exp->extravalue1); | ||||
| 		// bullshit copypaste orbit behavior
 | ||||
| 		if (exp->threshold) | ||||
| 		{ | ||||
| 			fixed_t orbit = (4*mo->scale) * (16 - exp->extravalue1); | ||||
| 
 | ||||
|             P_SetScale(exp, (exp->destscale = mapobjectscale - ((mapobjectscale/28) * exp->extravalue1))); | ||||
|             exp->z = exp->target->z; | ||||
|             P_MoveOrigin(exp, | ||||
|                 mo->x + FixedMul(orbit, FINECOSINE(exp->angle >> ANGLETOFINESHIFT)), | ||||
|                 mo->y + FixedMul(orbit, FINESINE(exp->angle >> ANGLETOFINESHIFT)), | ||||
|                 exp->z + mo->scale * 24 * P_MobjFlip(exp)); | ||||
| 			P_SetScale(exp, (exp->destscale = mapobjectscale - ((mapobjectscale/28) * exp->extravalue1))); | ||||
| 			exp->z = exp->target->z; | ||||
| 			P_MoveOrigin(exp, | ||||
| 				mo->x + FixedMul(orbit, FINECOSINE(exp->angle >> ANGLETOFINESHIFT)), | ||||
| 				mo->y + FixedMul(orbit, FINESINE(exp->angle >> ANGLETOFINESHIFT)), | ||||
| 				exp->z + mo->scale * 24 * P_MobjFlip(exp)); | ||||
| 
 | ||||
|             exp->momx = 0; | ||||
|             exp->momy = 0; | ||||
|             exp->momz = 0; | ||||
| 			exp->momx = 0; | ||||
| 			exp->momy = 0; | ||||
| 			exp->momz = 0; | ||||
| 
 | ||||
|             ghostme(exp, player); | ||||
| 			ghostme(exp, player); | ||||
| 
 | ||||
|             exp->angle += ANG30; | ||||
|             exp->extravalue1++; | ||||
| 			exp->angle += ANG30; | ||||
| 			exp->extravalue1++; | ||||
| 
 | ||||
|             if (exp->extravalue1 >= 16) | ||||
| 			if (exp->extravalue1 >= 16) | ||||
| 			{ | ||||
| 				if(P_IsDisplayPlayer(player)) // As you know Kris, I am FUCKING your EXP.
 | ||||
| 				{ | ||||
| 				S_StopSoundByID(exp->target, sfx_exp); | ||||
| 				S_StartSound(exp->target, sfx_exp); | ||||
|                 P_RemoveMobj(exp); | ||||
| 				} | ||||
| 				P_RemoveMobj(exp); | ||||
| 			} | ||||
| 
 | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
|         exp->angle += ANGLE_45/2; | ||||
| 		exp->angle += ANGLE_45/2; | ||||
| 
 | ||||
|         UINT8 damper = 3; | ||||
| 		UINT8 damper = 3; | ||||
| 
 | ||||
|         fixed_t vert = dist/3; | ||||
|         fixed_t speed = 60*exp->scale; | ||||
| 		fixed_t vert = dist/3; | ||||
| 		fixed_t speed = 60*exp->scale; | ||||
| 
 | ||||
|         if (exp->extravalue2) // Mode: going down, aim at the player and speed up / dampen stray movement
 | ||||
|         { | ||||
|             if (exp->extravalue1) | ||||
|                 exp->extravalue1--; | ||||
| 		if (exp->extravalue2) // Mode: going down, aim at the player and speed up / dampen stray movement
 | ||||
| 		{ | ||||
| 			if (exp->extravalue1) | ||||
| 				exp->extravalue1--; | ||||
| 
 | ||||
|             exp->extravalue2++; | ||||
| 			exp->extravalue2++; | ||||
| 
 | ||||
|             speed += exp->extravalue2 * exp->scale/2; | ||||
| 			speed += exp->extravalue2 * exp->scale/2; | ||||
| 
 | ||||
|             fakez = mo->z + (vert * exp->extravalue1 / EXP_ARCTIME); | ||||
|             damper = 1; | ||||
|         } | ||||
|         else // Mode: going up, aim above the player
 | ||||
|         { | ||||
|             exp->extravalue1++; | ||||
|             if (exp->extravalue1 >= EXP_ARCTIME) | ||||
|                 exp->extravalue2 = 1; | ||||
| 			fakez = mo->z + (vert * exp->extravalue1 / EXP_ARCTIME); | ||||
| 			damper = 1; | ||||
| 		} | ||||
| 		else // Mode: going up, aim above the player
 | ||||
| 		{ | ||||
| 			exp->extravalue1++; | ||||
| 			if (exp->extravalue1 >= EXP_ARCTIME) | ||||
| 				exp->extravalue2 = 1; | ||||
| 
 | ||||
|             fakez = mo->z + vert; | ||||
|         } | ||||
| 			fakez = mo->z + vert; | ||||
| 		} | ||||
| 
 | ||||
|         if (mo->flags & MFE_VERTICALFLIP) | ||||
|             fakez -= mo->height/2; | ||||
|         else | ||||
|             fakez += mo->height/2; | ||||
| 		if (mo->flags & MFE_VERTICALFLIP) | ||||
| 			fakez -= mo->height/2; | ||||
| 		else | ||||
| 			fakez += mo->height/2; | ||||
| 
 | ||||
|         hang = R_PointToAngle2(exp->x, exp->y, mo->x, mo->y); | ||||
|         vang = R_PointToAngle2(exp->z, 0, fakez, dist); | ||||
| 		hang = R_PointToAngle2(exp->x, exp->y, mo->x, mo->y); | ||||
| 		vang = R_PointToAngle2(exp->z, 0, fakez, dist); | ||||
| 
 | ||||
|         exp->momx -= exp->momx>>(damper), exp->momy -= exp->momy>>(damper), exp->momz -= exp->momz>>(damper); | ||||
|         exp->momx += FixedMul(FINESINE(vang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hang>>ANGLETOFINESHIFT), speed)); | ||||
|         exp->momy += FixedMul(FINESINE(vang>>ANGLETOFINESHIFT), FixedMul(FINESINE(hang>>ANGLETOFINESHIFT), speed)); | ||||
|         exp->momz += FixedMul(FINECOSINE(vang>>ANGLETOFINESHIFT), speed); | ||||
| 		exp->momx -= exp->momx>>(damper), exp->momy -= exp->momy>>(damper), exp->momz -= exp->momz>>(damper); | ||||
| 		exp->momx += FixedMul(FINESINE(vang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hang>>ANGLETOFINESHIFT), speed)); | ||||
| 		exp->momy += FixedMul(FINESINE(vang>>ANGLETOFINESHIFT), FixedMul(FINESINE(hang>>ANGLETOFINESHIFT), speed)); | ||||
| 		exp->momz += FixedMul(FINECOSINE(vang>>ANGLETOFINESHIFT), speed); | ||||
| 
 | ||||
|         ghostme(exp, player); | ||||
| 		ghostme(exp, player); | ||||
| 
 | ||||
|         if (dist < (EXP_ORBIT * exp->scale) && exp->extravalue2) | ||||
|         { | ||||
|             exp->threshold = TICRATE; | ||||
|             exp->extravalue1 = 0; | ||||
|             exp->extravalue2 = 0; | ||||
|         } | ||||
|     } | ||||
| 		if (dist < (EXP_ORBIT * exp->scale) && exp->extravalue2) | ||||
| 		{ | ||||
| 			exp->threshold = TICRATE; | ||||
| 			exp->extravalue1 = 0; | ||||
| 			exp->extravalue2 = 0; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -279,8 +279,16 @@ void Obj_JawzThrown(mobj_t *th, fixed_t finalSpeed, fixed_t dir) | |||
| 		th->momx = 0; | ||||
| 		th->momy = 0; | ||||
| 
 | ||||
| 		// Slow down the top speed.
 | ||||
| 		finalSpeed = FixedMul(finalSpeed, 4*FRACUNIT/5); | ||||
| 		// Return at absolutely 120% of the owner's speed if it's any less than that.
 | ||||
| 		fixed_t min_backthrowspeed = 6*(K_GetKartSpeed(owner, false, false))/5; | ||||
| 		if (owner->speed >= min_backthrowspeed) | ||||
| 		{ | ||||
| 			finalSpeed = 6*(owner->speed)/5; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			finalSpeed = min_backthrowspeed; | ||||
| 		} | ||||
| 
 | ||||
| 		// Set a fuse.
 | ||||
| 		th->fuse = RR_PROJECTILE_FUSE; | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ | |||
| #include "../doomdef.h" | ||||
| #include "../doomstat.h" | ||||
| #include "../info.h" | ||||
| #include "../g_game.h" | ||||
| #include "../k_objects.h" | ||||
| #include "../p_local.h" | ||||
| #include "../r_state.h" | ||||
|  |  | |||
|  | @ -215,18 +215,12 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) | |||
| 		else | ||||
| 		{ | ||||
| 			// Player Damage
 | ||||
| 			if ((t1->type == MT_ORBINAUT_SHIELD || t1->type == MT_JAWZ_SHIELD) | ||||
| 				&& !t2->player->invincibilitytimer && !K_IsBigger(t2, t1)) // UGH. Stumble ignores invinc. Fix this damage type someday.
 | ||||
| 			{ | ||||
| 				P_DamageMobj(t2, t1, t1->target, 1, DMG_WOMBO | DMG_STUMBLE); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				P_DamageMobj(t2, t1, t1->target, 1, DMG_WOMBO | | ||||
| 					(tumbleitem ? DMG_TUMBLE : DMG_WIPEOUT)); | ||||
| 				if (tumbleitem || (gametyperules & GTR_SPHERES) || !t2->player->tripwireLeniency) | ||||
| 			P_DamageMobj(t2, t1, t1->target, 1, DMG_WOMBO | | ||||
| 				(tumbleitem ? DMG_TUMBLE : DMG_WIPEOUT)); | ||||
| 
 | ||||
| 			if (tumbleitem || !t2->player->tripwireLeniency) | ||||
| 				if ((gametyperules & GTR_SPHERES) || (t1->type != MT_ORBINAUT_SHIELD && t1->type != MT_JAWZ_SHIELD)) | ||||
| 					K_KartBouncing(t2, t1); | ||||
| 			} | ||||
| 
 | ||||
| 			S_StartSound(t2, sfx_s3k7b); | ||||
| 		} | ||||
|  | @ -331,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; | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ | |||
| #define SPB_SLIPTIDEDELTA (ANG1 * 3) | ||||
| #define SPB_STEERDELTA (ANGLE_90 - ANG10) | ||||
| #define SPB_DEFAULTSPEED (FixedMul(mapobjectscale, K_GetKartSpeedFromStat(9) * 2)) | ||||
| #define SPB_ACTIVEDIST (1024 * FRACUNIT) | ||||
| #define SPB_ACTIVEDIST (2048 * FRACUNIT) | ||||
| 
 | ||||
| #define SPB_HOTPOTATO (2*TICRATE) | ||||
| #define SPB_MAXSWAPS (2) | ||||
|  |  | |||
|  | @ -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); } | ||||
|  | @ -115,7 +115,7 @@ struct Shoe : Mobj | |||
| 		shoe->dir(0); | ||||
| 		shoe->fuse = 15 * TICRATE; | ||||
| 
 | ||||
| 		INT32 numLinks = 8; | ||||
| 		INT32 numLinks = 5; | ||||
| 		Chain* link = nullptr; | ||||
| 
 | ||||
| 		for (INT32 i = 0; i < numLinks; ++i) | ||||
|  | @ -237,7 +237,7 @@ private: | |||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			thrust(a, 8 * mapobjectscale); | ||||
| 			thrust(a, 10 * mapobjectscale); | ||||
| 
 | ||||
| 			Fixed maxSpeed = 32 * mapobjectscale; | ||||
| 			Fixed speed = FixedHypot(momx, momy); | ||||
|  |  | |||
|  | @ -311,6 +311,7 @@ struct Cloud : Mobj | |||
| 		if (leveltime % (TICRATE/3) == 0 && follow()->player->rings > -20) // toxomister ring drain
 | ||||
| 		{ | ||||
| 			follow()->player->rings--; | ||||
| 			K_DefensiveOverdrive(follow()->player); | ||||
| 			S_StartSound(follow()->player->mo, sfx_antiri); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -321,7 +322,7 @@ struct Cloud : Mobj | |||
| 
 | ||||
| 		if (fuse < kMaxFuse && (kMaxFuse - fuse) % 20 == 0 && Mobj::valid(target()) && target()->player && follow()->player) | ||||
| 		{ | ||||
| 			K_SpawnAmps(target()->player, K_PvPAmpReward(3, target()->player, follow()->player), this); | ||||
| 			K_SpawnAmps(target()->player, K_PvPAmpReward(2, target()->player, follow()->player), this); | ||||
| 		} | ||||
| 
 | ||||
| 		follow()->player->stunned = fuse; // stunned as long as cloud is here
 | ||||
|  |  | |||
|  | @ -220,6 +220,7 @@ void Obj_playerWPZTurbine(player_t *p) | |||
| 	} | ||||
| 
 | ||||
| 	mt = t->spawnpoint; | ||||
| 	pmo->flags &= ~MF_NOGRAVITY; | ||||
| 
 | ||||
| 	opt1 = (mt->thing_args[0] != 0); | ||||
| 
 | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		
		Reference in a new issue